weakref.WeakValueDictionary
简介
很有趣的基础知识环节。
每个变量都有标识
,类型
,值
。每个对象标识只有一个,Python 里可以通过id
方法去查看这个标识的整数表示,类似指针。
别名可以有很多个,引用一个对象可以有多个别名。
>>> a=[1,2,[3,4]]
>>> b=a
>>> id(a)
140322604512904
>>> id(b)
140322604512904
>>>
==
表示比较两个对象的值(对象保存的数据)is
比较的是两个对象的标识通常我们关注的是值而不是标识,因此==
出现频率更高。
is
通常用在变量和单例值之间,比如
x is None
x is not None
有一个经典的例子:
>>> a=(1,2,[3,4])
>>> a[2]+=[5]
Traceback (most recent call last):
File "" , line 1, in
a[2]+=[5]
TypeError: 'tuple' object does not support item assignment
>>> a
(1, 2, [3, 4, 5])
元组的加法不是一步完成的。
这里可以深入理解一下:
a[2]
,即[3,4]
这个列表可修改,并且被修改了。a[2]
这个标识也不变,但是他的值变了。所以访问 a 的值的时候是有 5 的。
复制列表,即用内置构造方法是浅复制。
>>> a
[1, 2, [3, 4, 5]]
>>> b=list(a)
>>> id(a)
140322604513096
>>> id(b)
140322604513480
>>> [a[i] is b[i] for i in range(len(a))]
[True, True, True]
浅复制是什么呢?
构造方法或者是 [:]
,复制了最外层容器,副本中的元素是源容器中元素的引用。如果元素都是不可变的,还可以节省内存,如果可变,就容易出错。
出错的情况有很多:
>>> a=[1,2,(3,4)]
>>> b=a[:]
>>> print(a,b)
[1, 2, (3, 4)] [1, 2, (3, 4)]
>>> a.append(100)
>>> print(a,b)
[1, 2, (3, 4), 100] [1, 2, (3, 4)]
>>> b[2]+=(5,6)
>>> print(a,b)
[1, 2, (3, 4), 100] [1, 2, (3, 4, 5, 6)]
下述操作呢?
a 仍为 [1,2,(3,4),100],b 为 [1, 2, (3, 4, 5, 6)]
>>> a[0]=-1
>>> print(a,b)
[-1, 2, (3, 4), 100] [1, 2, (3, 4, 5, 6)]
>>> a=[1,2,[3,4]]
>>> b=a[:]
>>> a[2].append(10)
>>> a
[1, 2, [3, 4, 10]]
>>> b
[1, 2, [3, 4, 10]]
注意:
以上讲到的 Python 集合都是对象的引用,而 str,bytes,array.array等
单一序列是扁平的,直接作用在内存上保存数据本身,也可以用来解释上述第一点(修改值)。
直接有两个函数,copy
和deepcopy
,深复制会去迭代循环复制对象,因此有时候太深了会遇到麻烦,根据情况可以有根据情况去实现__copy__()
和__deepcopy__()
方法。参考copy
模块。
要慎用可变类型作为参数的默认值。显而易见,这里不再赘述。
方法很简单,以类为例,里面用self
的对象替代就好了。
class TwilightBus:
def __init__(self,passengers = None):
if passengers is None:
self.passengers = []
else:
self.passengers=passengers
def pick(self,name):
pass
def drop(self,name):
pass
del
语句删除名称,而不是对象。
若这个名称对应的对象在这个名称被删除后没有被引用的了,那么del
会将这个对象也删除。
一种垃圾回收的算法是计数,CPython
就是这么做的。显然当计数为0
时,对象会被销毁。当然有其他很多垃圾回收机制,如JPython
等。下面讲一下较为复杂一点的Python
的回收机制。
为 s1
创建一个回调函数,当这个引用的对象被销毁时,调用 bye()
函数。
>>> import weakref
>>> s1={1,2,3}
>>> s2=s1
>>> def bye():
print('Gone')
>>> ender=weakref.finalize(s1,bye)
>>> ender.alive
True
>>> del s1
>>> s2='another'
Gone
>>> ender.alive
False
可以观察到,当s1
被del
后,这个对象还在,因为被s2
引用了。当s2
重新引用到另一个地方的时候对象被销毁。
那么weakref.finalize
是怎么知道这个对象是否还存在的呢? 可以想到用一种类似缓存的思想去实现的,这里就是弱引用。
这里介绍弱引用的存在,不讨论如何实现。
导入weakref
包来看神奇的垃圾回收机制!
>>> import weakref
>>> a_set={0,1}
>>> wref=weakref.ref(a_set)
>>> wref
0x7f4885eab098; to 'set' at 0x7f4885e94128>
>>> wref()
{0, 1}
>>> a_set={2,3,4}
>>> wref()
{0, 1}
>>> wref() is None
False
>>> wref() is None
True
>>>
这里看得有点晕,首先要了解这是控制台会话,有一个特性是_
保存上一个表达式的值。且_
会自动赋值给None
的对象。
wref的所指对象
是a_set
。
a_set
改变时,因为_
仍然引用到{0,1},因此弱引用仍然存在。wref()
显然非Nonewref()
时,_
绑定到False了,这时{0,1}不再被引用,因此wref()
为空。weakref.WeakValueDictionary
简介多用于缓存。字面意思,实例化一个字典,值为弱引用了。如果用一个普通字典去映射到实例化的WVD
对象,再对普通字典进行操作,弱引用就用到了。
此外还有 weakref.WeakValueDictionary
,weakref.WeakSet
最后再讲一点有趣的东西,Python对不可变类型的欺骗:
str
,bytes
,frozenset
,tuple
等等,通过copy
,参数构造或[:]
都不会产生副本,这是Python为了节约内存做的内部优化。
>>> s1=(1,2,3)
>>> s2=s1[:]
>>> s1 is s2
True
>>> a='123'
>>> b='123'
>>> a is b
True
称为字符串驻留