资讯专栏INFORMATION COLUMN

Python中的对象引用、可变性和垃圾回收

ytwman / 2862人阅读

摘要:一对象引用基础知识变量是标注而不是容器。也就是说元组中不可变的是元素的标识,但元组的值会随着引用的可变对象变化而变化。在中每个对象的引用都会有统计。弱引用不会妨碍对象被当做垃圾回收。

导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。

本文重点:

1、明确变量保存的是引用这一本质;
2、熟悉对象引用的基础知识;
3、掌握深复制和浅复制;
4、熟悉函数传参引用时潜在的麻烦并避免。
一、对象引用基础知识

变量:是标注而不是容器。对引用式变量而言,是把变量分配给对象,反过来理解则不合理。

别名:同一个对象的不同标注就是别名,别名指向同一对象。

标识:可以把标识理解为对象在内存中的地址。每个变量都有标识、类型和值。对象一旦创建,它的标识绝不会变。is运算符比较两个对象的标识;id()函数返回对象标识的整数表示,即对象的内存地址。

相等性:用==运算符比较两个对象的值是否相等。注意a==b是语法糖,等同于a.__eq__(b)。

==和is的选择:我们关注值的频率比标识要高。

当比较变量和单例值的时候应该用is。例如:X is None 或 X is not None。

is运算符比==要快,因为is不能重载。

二、可变性 1、元组的相对不可变性:

指tuple数据结构的物理内容(即保存的引用)不可变。也就是说元组中不可变的是元素的标识,但元组的值会随着引用的可变对象变化而变化。
tuple,list,dict,set保存的是对象的引用,而str,byte,array.array保存的是对象的值(字符,字节,数字)。

2、浅复制与深复制

浅复制:当复制tuple,list,dict,set时,副本之间共享内部对象的引用。copy.copy()
深复制:当复制tuple,list,dict,set时,副本之间不共享内部对象的引用。copy.deepcopy()

eg:浅复制小例子

list1=[1,(55,66),[7,8,9]]
list2=list(list1)#构建副本默认为浅复制
list2[1]+=(77,88)#对元组进行+=运算会解绑list2[1],并与右端运算后的值之间重新绑定起来。
list2[2]+=[10]#对列表进行iadd运算会就地修改列表,不会发生重新绑定。
list2[0]*=3
list1[2].pop(0)
print(list1)
print(list2)
#输出:
[1, (55, 66), [8, 9, 10]]
[3, (55, 66, 77, 88), [8, 9, 10]]

分析:list2是list1的副本,我们对list2的三个元素均作了改动,但只有列表元素的改动影响到了list1。原因在于list1和list2的第三个列表元素共享引用,因此影响也会同步;元组因为发生了解绑的运算所以影响未同步到list1;至于数值的影响不同步的原因是因为浅复制针对str,byte,array.array这些对象直接将值重新保存到副本中来,不存在共享引用的内部逻辑。

深复制注意事项

深复制处在循环引用的对象时,深复制算法会进入无限循环中。

一些对象可能会引用不该复制的外部资源或单例值,这些对象的深复制的结果可能太深。

3、函数的参数作为引用时:

Python唯一支持的参数传递模式是共享传参。共享传参指函数的各个形式参数获得实参中各个引用的副本,即函数内部的形参是实参的别名。

函数可能会修改作为参数传入的可变对象。
(1)这个行为无法避免,除非在本地创建副本,或者使用不可变对象。

(2)因此在类中直接把参数赋值给实例变量之前一定要三思,因为这样会为参数对象创建别名,修改传入参数指向的可变对象。

eg:函数修改作为参数传入的全局变量

def f(a, b):
    a += b
    return a
a = [1, 2]
b = [3, 4]
f(a, b)
print(a, b)#输出[1, 2, 3, 4], [3, 4],此时列表a已经发生变化。
t = (10, 20)
u = (30, 40)
f(t, u)
print(t, u)#输出((10, 20), (30, 40)),此时元组t没有发生变化。

使用可变类型作为函数参数的默认值有危险。
原因在于包含此类函数的类的实例在未指定初始值时会使用同一个可变默认值。当一个实例就地修改参数时会影响其他实例对默认值的调用。

三、垃圾回收 1、垃圾回收的判定规则:

主要采用引用计数算法。
在Python中每个对象的引用都会有统计。当引用计数归零时,对象就会立即销毁。

除了循环引用外没有其他引用,处在循环引用的对象都会被销毁。

2、弱引用

某些情况下可能需要保存对象的引用,但不留存对象本身,此时可以借助弱引用实现。弱引用不会妨碍对象被当做垃圾回收。

弱引用是一种低层机制,是weakref模块中WeakValueDictionary、WeakKeyDictionary和WeakSet等有用的集合类,以及finalize函数的底层支持。

弱引用的局限性
弱引用所指对象可以是set,用户自定义的类,list和dict的子类。不可以是int、tuple的实例及子类,也不可以是list实例或dict实例。

四、Python对不可变类型施加的把戏

1、使用一个元组来构造另一个元组,得到的其实是同一个元组。

2、比较字符串或整数是否相等时,应该使用==而不是is。这是由于Python解释器内部驻留的特性所导致的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41476.html

相关文章

  • python 对象引用,变性垃圾回收

    摘要:每个变量都有标识类型和值对象一旦创建它的标识绝不会变标识可以简单的理解为对象在内存中的地址别名跟是别名指向如果增加新的内容也会增加相等性为运算符比较连个对象的值对象中保存的数据标识为因为他们都指向这个列表比较对象的标识元组相对不可变性元组保 a = [1,2,3,4] b = a 每个变量都有标识,类型和值.对象一旦创建,它的标识绝不会变;标识可以简单的理解为对象在内存中的地址. ...

    Flands 评论0 收藏0
  • Python学习之路27-对象引用变性垃圾回收

    摘要:函数的参数作为引用时唯一支持的参数传递模式是共享传参,它指函数的形参获得实参中各个引用的副本,即形参是实参的别名。而在上面这个例子中,类的属性实际上是形参所指向的对象所指对象,的别名。 《流畅的Python》笔记本篇是面向对象惯用方法的第一篇,一共六篇。本篇主要是一些概念性的讨论,内容有:Python中的变量,对象标识,值,别名,元组的某些特性,深浅复制,引用,函数参数,垃圾回收,de...

    Batkid 评论0 收藏0
  • 流畅的python读书笔记-第八章-对象引用变性垃圾回收

    摘要:运算符比较两个对象的标识函数返回对象标识的整数表示。实际上,每个对象都会统计有多少引用指向自己。对象被销毁了,调用了回调,的值变成了。当对象的引用数量归零后,垃圾回收程序会把对象销毁。引用的目标对象称为所指对象。 对象不是个盒子 showImg(https://segmentfault.com/img/bV95mW?w=1784&h=988); class Gizmo: def...

    zgbgx 评论0 收藏0
  • python对象引用变性垃圾回收

    摘要:对象引用和可变性变量不是盒子,而是便利贴变量的赋值方式比如是将一个变量分配给一个对象比如整数。运算符比较两个对象的标识函数返回对象标识的整数表示。每个对象都会统计有多少引用指向自己。对象被销毁执行回调函数输出 对象引用和可变性 变量不是盒子,而是‘便利贴’ >>> a = [1,2,3] >>> b = a >>> a.append(5) >>> a [1, 2, 3, 5] >>> ...

    chavesgu 评论0 收藏0
  • 万物之基础——对象

    摘要:每个对象均有标识符类型值。通常我们认为当这些对象被垃圾回收机制回收时,它占用的外部资源即被释放。造物主类型对象的类型几乎影响了该对象的所有功能,在某种程度上,对象的标识符也受其类型的影响。 原文地址 对象 对象(Objects)是python中数据的抽象,python中所有的数据均可以用对象或者是对象之间的关系来表示。每个对象均有标识符(identity)、类型(type)、值(val...

    douzifly 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<