黑马程序员技术交流社区

标题: 【西安校区】 Python对象引用、可变性和垃圾回收 [打印本页]

作者: 逆风TO    时间: 2019-9-19 09:13
标题: 【西安校区】 Python对象引用、可变性和垃圾回收
一、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对象
四、函数的参数作为引用时
Python 唯一支持的参数传递模式是共享传参(call by sharing) 。 多数面向对象语言都采用这一模式, 包括 Ruby、 Smalltalk 和 Java(Java 的引用类型是这样, 基本类型按值传参) 。
共享传参指函数的各个形式参数获得实参中各个引用的副本。 也就是说, 函数内部的形参是实参的别名。
这种方案的结果是, 函数可能会修改作为参数传入的可变对象, 但是无法修改那些对象的标识(即不能把一个对象替换成另一个对象) 。

>>> 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








欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2