1、可变序列和不可变序列
不可变序列(3个):
Number(数字) String(字符串) Tuple(元组)
可变序列(3个):
List(列表) Dictionary(字典) Set(集合)
对不可变序列来说,赋值和深浅拷贝都会新创建一个内存对象,因此,都属于深拷贝。
而对于可变序列来说、赋值和深浅拷贝对内存对象的处理是存在差异的。
2、可变序列的赋值和拷贝
| 赋值方式 | 结果 | 实例 |
1 | 直接用=号赋值 | 创建引用,无拷贝,仅用新的变量指向同一个对象 | List1=[1,2,3,4] List2=list1 #两个list指向同一个内存对象 |
2 | 函数参数传递 | 创建引用,无拷贝,仅用新的变量(别名)指向同一个对象 | 函数内部可以修改作为参数传入的可变对象,而无法修改作为参数传入的不可变对象。不要使用可变对象作为函数参数的默认值,因为随着函数的调用,参数的默认值有可能被修改。 |
3 | 调用对象的内置copy函数 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | List1=[1,2,[3,4,5],6] List2=list1.copy() #list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
4 | 使用列表或字典生成式 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | List1=[1,2,[3,4,5],6] List2=[I for I in list1] #list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
5 | 使用for循环 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | List1=[1,2,[3,4,5],6] List2=[] For I in list1: List2.append(i) #list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
6 | 使用切片 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | List1=[1,2,[3,4,5],6] List2=list1[:]#list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
7 | 使用构造函数 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | List1=[1,2,[3,4,5],6] List2=list(List1)#list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
8 | 使用copy模块的copy()函数 | 浅拷贝,仅拷贝当前层不可变变量和当前层可变变量的地址 | Import copy List1=[1,2,[3,4,5],6] List2=copy.copy(list1) # list2和list1指向不同的内存对象,但两个内存对象中的列表指向同一个内存地址 |
9 | 使用copy模块的deepcopy()函数 | 深拷贝,实现所有可变和不可变变量的值拷贝 | Import copy List1=[1,2,[3,4,5],6] List2=copy.deepcopy(list1) # list2和list1指向不同的内存对象 |
3、== 和 is
== 运算符比较两个对象的值(对象中保存的数据),而 is 比较对象的标识(id()返回的地址)。
通常,我们关注的是值,而不是标识,因此 Python 代码中 == 出现的频率比 is 高。
然而,在变量和单例值(不可变序列)之间比较时,使用 is速度更快。
这里需要注意的是:值相同的不可变序列变量,在内存中指向同一个对象,因此其ID相同。
例如:x =’abc’ y =’abc’ x is y 值是True
目前,最常使用 is检查变量绑定的值是不是 None。
x is None
x is not None
is 运算符比 == 速度快,因为它不能重载,所以 Python 不用寻找并调用特殊方法,而是直接比较两个整数 ID。
而 a == b 是语法糖,等同于a.__eq__(b)。继承自 object 的 __eq__ 方法比较两个对象的 ID,结果与 is 一样。但是多数内置类型使用更有意义的方式覆盖了 __eq__方法,会考虑对象属性的值。相等性测试可能涉及大量处理工作,例如比较大型集合或嵌套层级深的结构时。
4、弱引用
在 CPython 中,垃圾回收使用的主要算法是引用计数。实际上,每个对象都会统计有多少引用指向自己。当引用计数归零时,对象立即就被销毁:CPython 会在对象上调用 __del__ 方法(如果定义了),然后释放分配给对象的内存。
弱引用不会增加对象的引用数量。引用的目标对象称为所指对象(referent)。因此我们说,弱引用不会妨碍所指对象被当作垃圾回收。
弱引用在缓存应用中很有用,因为我们不想仅因为被缓存引用着而始终保存缓存对象。
WeakValueDictionary 类实现的是一种可变映射,里面的值是对象的弱引用。被引用的对象在程序中的其他地方被当作垃圾回收后,该对象对应的key会自动从 WeakValueDictionary 中删除。因此,WeakValueDictionary 经常用于缓存。
弱引用的局限性:
不是每个 Python 对象都可以作为弱引用的目标(或称所指对象)。
- 基本的 list 和 dict 实例不能作为所指对象,但是它们的子类可以
- int 和 tuple 实例不能作为弱引用的目标,甚至它们的子类也不行
- set 实例可以作为所指对象
- 用户定义的类型也没问题
weakref模块其它常用的方法如下
refobj=weakref.ref(object[, callback]) | 创建单个对象object的弱引用对象,callback是object被销毁时调用的回调函数,该函数以被销毁的弱引用对象为参数。通过用这种方式创建的弱引用对象访问object时,要使用调用的方式:refobj() / refobj().类成员 |
refobj=weakref.proxy(object[, callback]) | 通过代理的方式创建弱引用对象。该函数功能、参数和weakref.ref相同,不同点: 返回的是object弱引用代理,不需要通过调用的方式访问object: refobj / refobj.类成员 |
weakref.getweakrefcount(object) | 返回对象的弱引用个数 |
weakref.getweakrefs(object) | 以列表的方式返回对象的所有弱引用 |
refdict=weakref.WeakKeyDictionary([dict]) refdict. keyrefs()返回所有key的弱引用对象组成的列表, 访问key的弱引用对象的值用调用方式。 | 为字典的key创建弱引用,当key引用为0时回收,注意key需要是可以被弱引用类型。 |
refdict=weakref.WeakValueDictionary([dict]) refdict. valuerefs()返回所有value的弱引用对象组成的列表, 访问value的弱引用对象的值用调用方式。 | 为字典的value创建弱引用,当value引用为0时回收,注意value需要是可被弱引用类型。
|
weakref.WeakSet([elements]) | 对集合类创建弱引用,保持对其每个成员的弱引用 |
实例1:weakref.ref
import weakref
class AAA:
def __init__(self,value):
self.value = value
def fun(key):
print('object a is deleted',key)
a = AAA('abc')
x = weakref.ref(a,fun) #创建弱引用
print(x().value) #访问原对象的值使用调用的方式
del a #销毁原对象时,调用weakref.ref指定的回调函数fun
实例2:weakref. proxy
import weakref
class AAA:
def __init__(self,value):
self.value = value
def fun(key):
print('object a is deleted',key)
a = AAA('abc')
x = weakref.proxy(a,fun) #创建弱引用
print(x.value) #访问原对象的值使用直接访问的方式
del a #销毁原对象时,调用weakref. proxy指定的回调函数fun
实例3:WeakKeyDictionary
import weakref
class AAA:
def __init__(self,value):
self.value = value
def fun(key):
print('object a is deleted',key)
a = AAA('aaa')
b = AAA('bbb')
c = AAA('ccc')
abc = {a:1,b:2,c:3}
refabc = weakref.WeakKeyDictionary(abc) #创建字典的弱引用,字典的key必须是可被引用的
print(refabc)
for item in refabc.keyrefs(): #获得字典的key组成的被引用对象列表
print(item().value) #访问key的值使用调用的方式