Python对象生命周期

生命周期:一个对象,从诞生到消亡的过程。当一个对象被创建时,会在内存中分配相应的内存空间进行存储。当这个对象不在使用时,为了节约内存,就会把这个对象释放

监听对象的生命过程

# __new__方法:当我我们创建一个对象的时候,用于给这个对象分配内存的方法
# 通过拦截这个方法,可以修改对象的创建过程,例如单例模式
#  __init__方法:相当于构造函数,创建时自动调用
# __del__方法,相当于析构函数,自动调用

小案例如下:

"""
实现功能:记录Person类产生了多少个实例,有多少个
"""
class Person:
    __count = 0
    def __init__():
        Person.__count += 1
        pass
    def __del__(self):
        Person.__count -= 1
        pass
    @classmethod
    def log(cls):
        print("count:%d" %cls.count)
        pass
    pass

Python是如何掌握一个对象的生命?

内存管理机制

# python会根据不同的类型以及内容,开辟不同的空间大小进行存储,返回该空间的地址给外界接收(称为“引用”),用于后续对这个对象的操作
# 可通过id()函数获取内存地址(10进制)
# 通过hex函数查看对应的16位地址
# 对于整数和短小的字符,Python会进行缓存,不会创建多个对象
num1 = 2
num2 = 2
print(id(num1))
print(id(num2))
# 这里num1  num2的地址是一样的
# 容器对象,存储其他对象,仅仅时其他对象对象的引用,而不是其他对象本身

对象内存关系图如下:

python 变量生命周期 id相同 python对象生命周期_python 变量生命周期 id相同

垃圾回收机制

  • 引用计算器:一个对象,会记录这自身被引用的个数,增加一个引用,加一,减少一个引用,减一,当引用个数为0时,系统会自动回收掉该内存,查看方式如下:
import sys
sys.getrefcount(ZhangSan) # 注意:使用这种方法时,会增加一次引用,所以到时候必须减一
# +1的场景:对象被创建   对象被引用(P1 = P2)对象被作为参数,传入到一个函数中(会+2)  对象作为一个元素,存储再容器中
# -1的场景:对象被del  对象的引用被赋予其他值(p1 = 123)  一个对象离开它的作用域(一个函数执行完毕时 或 内部的局部变量关联的对象,它的引用会被自动取消) 对象所在的容器被销毁,或从容器中删除对象
  • 循环引用问题:图示如下:其中我们可以发现,这两个对象存在了互相引用的关系,但如果我del掉这两个对象,这两个对象并不会释放内存,就会存在内存被无效占用的情况,这是十分可惜的
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hw02zJ4l-1640334304946)(python面向对象生命周期.assets/1640331091300.png)]
import objgraph
# 这里是一个新加入的包,包通过setting中的project 中的第一选项,中的➕进行添加
# objgraph.count("类名") 可以查看,垃圾回收机制中对象的引用次数 跟踪对象的个数
# 内存管理机制 = 引用计算器机制 + 垃圾回收机制

垃圾回收机制

# 主要作用:从经历过“引用计数器机制”依然未被释放的对象中,找到“循环引用”,干掉相关对象
# 底层机制(了解):
# 1.收集所有的“容器对象”(可以引用其他对象的对象,基础类型不算),通过一个双向链表进行引用
# 2.针对每一个“容器对象”,通过一个变量gc_refs来记录当前对应的引用计数
# 3.对于每个“容器对象”,找到它引用的“容器对象”,并将这个“容器对象”的引用计数 -1
# 4.经过步骤3之后,如果一个“容器对象”的引用计数为0,就代表可以被回收了,肯定是“循环引用”
# 但如果每次都把所有对象检查一边,就会很浪费性能,所以python采用了一个名为“分代回收”机制(思想)
# 1.首先默认一个对象被创建出来以后,属于0代
# 2.如果经历这一代“垃圾回收”后,依然存活,则划分到下一代
# 3.“垃圾回收”的周期顺序为:0代”垃圾回收“达到一定次数,会触发 0代和1代回收 1代"垃圾回收"达到一定次数,会触发0代,1代和2代回收
# 再垃圾回收器中,新增的对象个数- 消亡的对象个数,达到一定的阈值时,才会触发垃圾检测
# 查看和设置相关参数
import gc
print(gc.get_threshold())
gc.set_threshold(70,10,5)  # 相差700时开始检测,10  5 触发一代和二代 
# 自动回收
# 1.开启垃圾回收机制
gc.enable() # 开启垃圾回收机制(默认开启)
gc.disable() # 关闭垃圾回收机制
gc.isenabled() # 判定是否开启
# 手动回收:
import gc
gc.collect()  # 运行回收垃圾

# 阈值:
gc.get_threshold() # 获取自动自动回收阈值
gc.set_threshold() # 设置自动回收阈值
# 版本兼容问题:
# python 3.x版本如上
# python 2.x版本:一旦有一个对象实现了del方法,系统便不会自动帮我们释放
# 解决方案1:尽量避免循环应用
# 解决方案2:采用软引用(即计算器不会加1),例如:
import weakref
import 
p.pet = d                           # d的引用次数为2,但删除p以后,d会减一
gc.set_threshold(70,10,5)
p.pet = {"dog":weakref(d1),"cat":weakref(c1)}
# 当像上面那样引用对象较多时,可以采用字典
# weakref。WeakValueDictionary({"dog": d1,"cat": c1})