Python深拷贝与浅拷贝精讲

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 对象都可以作为弱引用的目标(或称所指对象)。

  1. 基本的 list 和 dict 实例不能作为所指对象,但是它们的子类可以
  2. int 和 tuple 实例不能作为弱引用的目标,甚至它们的子类也不行
  3. set 实例可以作为所指对象
  4. 用户定义的类型也没问题

 

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的值使用调用的方式

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值