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

python变量类似于引用式变量,因此可以理解为附加在对象上的标注

charles = {"name": 'charles', "age": 30}
lews = charles
print(charles==lews)
print(charles is lews)

元组的相对不可变性:

集合,列表,字典等python集合保存的式=是对象的引用,元组的不可变性指的是tuple数据结构的物理内容(及保存的引用)不可变,与引用的对象无关。

t1 = (1, 2, [3, 4])
t2 = (1, 2, [3, 4])
print(t1 == t2)   # true
print(t1 is t2)   # false
print(id(t1[-1]))
t1[-1].append(5)
print(t1)
print(id(t1[-1]))  # 依然不可变 这才是元组不可变的本质

副本与源对象, 浅复制

复制列表等可变对象,有两种方法:

# method 1
a = [12, 23, [11, 22], (7, 8, 9)]
b = list(a)    # a是源对象, b是副本

# method 2
b = a[:]    # 复制

copy模块提供的copy()和deepcopy()能为任意对象做求浅复制和深复制

from copy import copy, deepcopy


class Bus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)

    def drop(self, name):
        self.passengers.remove(name)

    def pick(self, name):
        self.passengers.append(name)

    def __repr__(self):
        return "Passengers are: " + str(self.passengers)


if __name__=="__main__":
    passenger = ["Lily", "Nosy", "Tom"]
    bus1 = Bus(passenger)
    bus2 = copy(bus1)      # copy函数
    bus3 = deepcopy(bus1)   # deepcopy函数
    print(bus1)

    bus1.pick("liu")
    print(bus1)
    print(bus2)
    print(bus3)




输出结果:

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

此外,可以通过实现特殊方法__copy__()和__deepcopy__()能够控制copy的行为。

参数传递

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

可对上面的程序做一个小的修改

from copy import copy, deepcopy


class Bus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            # self.passengers = list(passengers)   # 相当于创建实参的一个副本,所以形参变化,实参不变化
            self.passengers = passengers   # 形参是实参的别名,python中参数传递的方式

    def drop(self, name):
        self.passengers.remove(name)

    def pick(self, name):
        self.passengers.append(name)

    def __repr__(self):
        return "Passengers are: " + str(self.passengers)


if __name__=="__main__":
    passenger = ["Lily", "Nosy", "Tom"]
    bus1 = Bus(passenger)
    bus2 = copy(bus1)      # copy函数
    bus3 = deepcopy(bus1)   # deepcopy函数
    print(bus1)

    bus1.pick("liu")
    print(bus1)
    print(bus2)
    print(bus3)
    print(passenger)   # pick方法对函数内部的形参做了修改
    # 形参是实参的引用,所以说实参也会跟着变化

不同类型的对象的实参,传入函数中后,会受到不同的影响

1. 数组和元组没变

2. 列表变了

不适用可变类型作为函数的默认值

可选参数可以有默认值,这是python的一个特性,能保证API在进化的时候向后兼容, 但是,应该避免使用可变的对象作为参数的默认值。

例如对上面的代码作如下修改:

    passenger = ["Lily", "Nosy", "Tom"]
    bus1 = Bus(passenger)
    bus1.pick("wang")
    bus1.drop("Lily")
    print(bus1)

    bus2 = Bus()   # 使用了默认的参数值
    print(bus2)
    bus2.pick("zhang")

    bus3 = Bus()   # 使用默认参数,但此时默认的参数(可变对象)已经被修改
    print(bus3)    # 修改了之后。bus3中有值了

输出结果:

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

在修改bus2的时候,实际上修改了传递给构造方法的那个列表,所以bus3中出现了一个“幽灵” 乘客。

正确的做法是:Bus类应该自己维护passenger列表,应该做到在构造方法中将实参的副本传递给实参。这样就不会影响初始化的时候传递的参数了。

del和垃圾回收:

del语句删除名称,而不是对象,del命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用的时候。

cpython中,垃圾回收采用的算法是引用计数,实际上,每个对象都会统计有多少个引用指向自己,当引用计数归零时,对象立即会被销毁。

原文链接: https://www.cnblogs.com/ncepubye/p/12724046.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

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

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/396647

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月6日 上午11:26
下一篇 2023年4月6日 上午11:26

相关推荐