- 对象是在内存中实实在在的,在内存中有一个地址存放他的内容的。
- 引用可以理解成对这个对象的地址,或者这个对象的名字
python中有一句话是“python中一切都是对象”,整数在python中是一个整数对象。整数相当于java中的Integer类,而不是int。
a = 1 // 1是对象,a是这个对象的引用
在python中有两个运算符 == 和 is,前者用于比较对象的值,后者用于比较两个对象是不是同一个
例子:
““
a=1000
b=1000
a == b
True
a is b
Falseid(a)
140524223738936
id(b)
140524223738984
id(1000)
140524223738864
点评:
a指向一个对象1000 ,b指向另外一个对象1000
a 和 b 值相等,但是两者不是同一个对象的引用
创建了两个对象1000,
通过id()可以获取一个对象在内存中地址
““
例子:
““
a=1000
b=a
a == b
True
a is b
True
id(a)
140524223738864
id(b)
140524223738864
id(1000)
140524223738960
点评:
第二步将a赋值给b,实际效果相当于b和a同时指向同一个对象1000,因此id(a)和id(b)相等,a is b 返回True
““
例子:
““
a=2
b=2
a == b
True
a is b
True
id(a)
140524220614464
id(b)
140524220614464
id(2)
140524220614464a=”a”
b=”a”
id(a)
4471174648
id(b)
4471174648
id(“a”)
4471174648
点评:
对于[-5,255]内的整数和单字符的字符串在python启动的时候就会创建好对象,因此a=2和b=2实际都只有一个对象2(节省了内存申请和释放的开销)
对于大整数都是实时创建和销毁的,如果要长期保存大整数会有内存问题,事实上python中整数对象池是只增不减的。如果代码运行到某一时刻同时存在成千上万的整数对象,那么之后即使不用了,内存也会居高不下,这点在数值计算时需要特别注意
上面一个例子中id(1000)和id(a)不同,代表a=1000创建了2个对象,分配了2块内存,一个是1000,另一个是指向对象1000的引用a。
而本例中从id(a)和id(2)相同,why???
““
例子:容器里的对象
python中存放的一切对象都是引用。
((1111,2222,3333),(4444,5555,6666))
这个元祖在内存中布局是:
1. 整体上这是一个元祖,里面的两个元素也都是元祖,因此内存中会有2个内存块存放这两个元祖的地址
2. 每一个元祖元素又包含3个整数对象,因此会有3个内存块存放三个整数对象的引用,另外还会有3个内存块存放整数对象本身
例子:extend和+=的区别
- 区别1:
>>> import dis
>>> def f1():
... l1+=[2]
...
>>> dis.dis(f1)
2 0 LOAD_FAST 0 (l1)
3 LOAD_CONST 1 (2)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (l1)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
>>>
>>> def f2():
... l1.extend([2])
...
>>> dis.dis(f2)
2 0 LOAD_GLOBAL 0 (l1)
3 LOAD_ATTR 1 (extend)
6 LOAD_CONST 1 (2)
9 BUILD_LIST 1
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
>>>
- 共同点:
>>> l1=[]
>>> l1+=t1
>>> l1
[1, 2, 3]
>>> l1.extend(t1)
>>> l1
[1, 2, 3, 1, 2, 3]
说明:
1. dis模块可以将python代码反编译出来,写成类似汇编指令的形式。
2. += 是个内置的运算操作,extend是个函数,涉及到函数查找,入栈出栈,前者效率更高些
3. +=和extend达到的效果一样,都是对list的本地延伸,后面被+=的对象或者extend的参数只要可以遍历即可,不一定是list,可以是tuple等
例子:不可变对象
>>> t=(1,2)
>>> id(t)
4553455016
>>> t2=(3,)
>>> t+=t2
>>> t
(1, 2, 3)
>>> id(t)
4553522144
>>>
1. python中不可变对象只有: int ,float,str,tuple,complex。
2. 要想改变这些对象只有新建一个新的对象,将引用指向新的对象,实际原来的对象本身并没有改变。因此当使用tuple来做需要经常改变内容的数组,会造成很大的效率问题
3. 不能对不可变对象的元素进行赋值,如下:
>>> t[0]=5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>
4. 字典的key值是必须是不可变对象,
>>> l=[]
>>> t=(1,)
>>> d={t:"123"}
>>> d={l:"123"}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
例子:
>>> t=(1)
>>> type(t)
<type 'int'>
>>> t=(1,)
>>> type(t)
<type 'tuple'>
说明:
只有一个元素的tuple,该元素后要加一个逗号,否则就是个整数类型,而不是tuple
例子:
>>> l=[[]]*9
>>> l
[[], [], [], [], [], [], [], [], []]
>>> l=[[]]*9
>>> l
[[], [], [], [], [], [], [], [], []]
>>> l[0].append(1)
>>> l
[[1], [1], [1], [1], [1], [1], [1], [1], [1]]
>>> l[1].append(2)
>>> l
[[1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2]]
>>>
说明:
1、 l中的每个元素都是list,因此都是引用,*9实际是将第一个元素的引用复制了9次。因此改变了l[0],大家都变了
2. python中对象的赋值,传递都是引用的传递赋值。除非使用copy库中的接口,可以强制指定复制
3. [[]]*9 等价于 :
l=[]
t=[]
for i in xrange(9):
l.append(t)
要想实现9个不同的空列表,可以这样:
l=[]
for i in xrange(9):
l.append([]) //每次都会创建一个新的[]
简写成:[[] for i in xrange(9)]
4.
>>> l=[[]]
>>> id(l)
4553610736
>>> l=l*10 //赋值操作意味着两件事儿,new一个对象,
>>> id(l)
4553611312
>>> l=[[]]
>>> id(l)
4553610088
>>> l *= 10
>>> id(l)
4553610088
>>>
*= 操作 id不变,
例子:
>>> def f(a,L=[]):
... L.append(a)
... print L
...
>>> f(1)
[1]
>>> f(2)
[1, 2]
>>>
说明:
L的默认值是和f绑定在一起的,每次调用f的时候都用L来引用,这个列表是在建立f的时候创建的。
python在碰到def时,会将下面的“代码对象”关联到函数名的引用。
每次执行的时候只是将L引用到这个对象,并不会重新建立一个新的列表然后将引用赋给L
例子:
def f(g):
… g()
…
def h():
… print “I am h”
…
f(h)
I am h
说明:
函数def和变量赋值本质上没有任何区别。函数f就是对一个函数对象的引用,跟a=1是表示a对对象1的引用一样。
因此可以执行g=f,通过g()来调用f(),另外函数对象也可以像其他对象一样作为参数传递