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

目录

一、Python中的变量是什么

 二、==和is的区别

三、del语句和垃圾回收

四、函数的参数作为引用时

2、不要使用可变类型作为参数的默认值


一、Python中的变量是什么

Python和java中的变量本质不一样。java中的变量是一个盒子,声明时已经说明了盒子的类型,大小。Python的变量实质是一个指针。也可以理解成一个便利贴。可以贴在任何类型上面。

>>> a = [1, 2, 3]
>>> b = a
>>> a.append(4) #操作a实际上也就是操作b
>>> b
[1, 2, 3, 4]

 如果把变量想象为盒子, 那么无法解释 Python 中的赋值;应该把变量视作便利贴, 这样就好解释了。先 生成变量,才把便利贴贴上。


为了理解Python中的赋值语句,应该始终先读右边,对象在右边创建或获取,在此之后左边的变量才会绑定到对象上,这就像为对象贴上标注。

 二、==和is的区别

== 运算符比较两个对象的值(对象中保存的数据) , 而 is 比较对象的标识。

a = [1,2,3]
b = a
print (id(a), id(b))
print (a is b)


通常, 我们关注的是值, 而不是标识, 因此 Python 代码中 == 出现的频率比 is 高。然而, 在变量和单例值之间比较时, 应该使用 is。
 

class People:
    pass

person = People()
if type(person) is People:
    print ("yes")

 

is 运算符比 == 速度快, 因为它不能重载, 所以 Python 不用寻找并调用特殊方法, 而是直接比较两个整数 ID。 而 a == b 是语法糖, 等同于a.__eq__(b)。
 

三、del语句和垃圾回收

del 语句删除名称, 而不是对象。 del 命令可能会导致对象被当作垃圾回收, 但是仅当删除的变量保存的是对象的最后一个引用, 或者无法得到对象时重新绑定也可能会导致对象的引用数量归零, 导致对象被销毁。

如果两个对象相互引用, 当它们的引用只存在二者之间时, 垃圾回收程序会判定它们都无法获取, 进而把它们都销毁。
 

在 CPython 中, 垃圾回收使用的主要算法是引用计数。 实际上, 每个对象都会统计有多少引用指向自己。 当引用计数归零时, 对象立即就被销毁: CPython 会在对象上调用 __del__ 方法(如果定义了) , 然后释放分配给对象的内存。 CPython 2.0 增加了分代垃圾回收算法。
 

a = object()
b = a
del a
print(b)
print(a)  #无法输出a对象

四、函数的参数作为引用时

  1. Python 唯一支持的参数传递模式是共享传参(call by sharing) 。 多数面向对象语言都采用这一模式, 包括 Ruby、 Smalltalk 和 Java(Java 的引用类型是这样, 基本类型按值传参) 。
  2. 共享传参指函数的各个形式参数获得实参中各个引用的副本。 也就是说, 函数内部的形参是实参的别名。
  3. 这种方案的结果是, 函数可能会修改作为参数传入的可变对象, 但是无法修改那些对象的标识(即不能把一个对象替换成另一个对象) 。
     
>>> def f(a, b):
... a += b
... return a
...
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y 
(1, 2)
>>> a = [1, 2] #可变对象
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b 
([1, 2, 3, 4], [3, 4])
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u 
((10, 20), (30, 40))

2、不要使用可变类型作为参数的默认值

可选参数可以有默认值, 这是 Python 函数定义的一个很棒的特性, 这样我们的 API 在进化的同时能保证向后兼容。 然而, 我们应该避免使用可变的对象作为参数的默认值。

class Company:
    def __init__(self, name, staffs=[]):
        self.name = name
        self.staffs = staffs
    def add(self, staff_name):
        self.staffs.append(staff_name)
    def remove(self, staff_name):
        self.staffs.remove(staff_name)

if __name__ == "__main__":
    com1 = Company("com1", ["tian1", "tian2"])
    com1.add("tian3")
    com1.remove("tian1")
    # print (com1.staffs)

    com2 = Company("com2")
    com2.add("tian")
    # print(com2.staffs)
    #
    print (Company.__init__.__defaults__) #(['tian'],)
    #
    com3 = Company("com3")
    com3.add("tian5")
    print (com2.staffs) #['tian', 'tian5']
    print (com3.staffs) #['tian', 'tian5']
    print (com2.staffs is com3.staffs)  #True 问题在于, 没有指定初始员工的 Company实例会共 
                                                                        #享同一个乘客列表
    # #com2和com3共用一个默认的空list

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值