变量不是盒子
Python
变量就像Java
的引用变量。Python
变量应该当成是对象的标签
定义,相等,别名
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| charles = {'name': 'Charles L. Dodgson', 'born': 1832}
lewis = charles print(lewis is charles)
print(id(charles), id(lewis))
lewis['balance'] = 950 print(charles)
alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance': 950} print(alex == charles) print(alex is charles)
|
charles
和lewis
引用的是相同的对象
alex
对象的内容与charles
相同,但不是引用相同的对象,Python
的==
,比较的是对象的属相。is
操作符比较的是对象的id
,对象的id
是创建时就确定了,永远不会改变了。
==和is
==
操作符比较对象的值
判断对象是否为None
,更合理的方式是
is
操作符速度比==
快,因为它不能被重载,因此Python
不用寻找特殊的方法来计算它。只需要简单的比较它们的id
就可以了。对于a==b
,其实背后使用的是a.__eq__(b)
,__eq__
是从Object
继承用于比较对象的id
,因此效果和is
一样.但是很多内置的类型重载了__eq__
,做了更多的操作比较对象属性的值。因此==
可能会产生额外的计算。
Tuple相对不可修改
元组跟大多数的Python
的集合–lists,dicts,sets
一样,都是持有对象们的引用。如果引用类型和修改,那么元组自身不可修改,而内部条目可以修改。
1 2 3 4 5 6 7
| t1 = (1, 2, [30, 40]) t2 = (1, 2, [30, 40]) print(t1 == t2) print(id(t1[-1])) t1[-1].append(99) print(t1) print(t1 == t2)
|
t1
是不可修改的,t[-1]
可修改
t1
和t2
的内容一样。修改t2
的条目的内容,这个时候t1
和t2
不相同了
默认浅拷贝
1 2 3 4 5 6 7
| l1 = [3, [55, 44], (7, 8, 9)] l2 = list(l1) print(l2) print(l1 == l2) print(l1 is l2)
l3 = l1[:]
|
l2
由l1
拷贝,值相等,但对象不相同
l3
由l1
拷贝
- 两种方式的拷贝都属于浅拷贝。
1 2 3 4 5 6 7 8 9 10
| l1 = [3, [66, 55, 44], (7, 8, 9)] l2 = list(l1) l1.append(100) l1[1].remove(55) print('l1:', l1) print('l2:', l2) l2[1] += [33, 22] l2[2] += (10, 11) print('l1:', l1) print('l2:', l2)
|
l2
是l1
的浅拷贝。l1[1]
移除55。这也会影响到l2
任意对象的深浅拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Bus: def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers)
def pick(self, name): self.passengers.append(name)
def drop(self, name): self.passengers.remove(name)
import copy
bus1 = Bus(['Alice', 'Bill', 'Claire', 'David']) bus2 = copy.copy(bus1) bus3 = copy.deepcopy(bus1)
print(id(bus1), id(bus2), id(bus3))
bus1.drop('Bill') print(bus1.passengers) print(bus2.passengers) print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)) print(bus3.passengers)
|
bus2
是bus1
的浅拷贝,bus3
是bus1
的深拷贝
删除和垃圾回收
del
表达式仅删除名字,不会删除对象。执行del
表达式之后,对象可能会被回收。但只有在删除了最后一个引用对象的变量,才会被回收掉。CPython
垃圾回收的基本算法是引用计数。CPython2.0
,引入了新的垃圾回收算法,可以删除循环引用的对象组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| s1 = {1, 2, 3} s2 = s1
def bye(): print('Gone with the wind')
ender = weakref.finalize(s1, bye) print(ender.alive) del s1 print(ender.alive) s2 = 'spam' print(ender.alive)
|
s1
,s2
指向相同的对象
- 使用
weakref
来监控s1
的状态
- 执行
del s1
,对象还被s2
引用没有被回收
- 改变
s2
的引用,这个时候对象被回收
弱引用
弱引用不增加引用计数。弱引用不阻止垃圾回收
1 2 3 4 5 6 7 8 9 10
| a_set = {0, 1} wref = weakref.ref(a_set) print(wref) print(wref())
a_set = {2, 3, 4} print(wref()) print(wref() is None) print(wref() is None)
|
WeakValueDictionary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Cheese: def __init__(self, kind): self.kind = kind
def __repr__(self): return 'Cheese(%r)' % self.kind
import weakref
stock = weakref.WeakKeyDictionary() catalog = [Cheese('Red Leicester'), Cheese('Tilsit'), Cheese('Brie'), Cheese('Parmesan')]
for cheese in catalog: stock[cheese.kind] = cheese
print(sorted(stock.keys()))
del catalog print(stock.keys()) del cheese print(sorted(stock.keys()))
|
弱引用的限制
不是所有的Python
对象都能使用弱引用。基本list
和dict
都不能被引用。但它们的纯净子类可以。Set
也可以