一、区别和联系
__new__是构造函数,在创建实例化对象时调用(触发),有一个必要的cls参数,代表当前类,__new__必须要有返回值。__init__是初始化函数,在创建实例化对象后调用(触发),有一个必要的self参数,代表当前实例对象,__init__不需要有返回值。总之,两者都在创建实例对象触发,__new__创建实例对象,__init__初始化实例对象的属性
二、__init__和__new__触发效果展示
class Test(object): def __new__(cls, *args, **kwargs): print("__new__被调用了") return super().__new__(cls) def __init__(self): print("__init__被调用了") super(Test,self).__init__() obj = Test()
__new__被调用了
__init__被调用了
三、__new__实现单例模式
cls本身没有_instance属性,所以我们声明了一个类属性_instance
class Alone_Model(object): _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = object.__new__(cls) return cls._instance def __init__(self, name, age): self.name = name self.age = age obj = Alone_Model('bang',25) obj2 = Alone_Model('he',36) print(obj is obj2) # True print(id(obj),id(obj2)) # 内存地址一样 print(obj.name,obj2.name) # he,he obj2更新了初始化,由于单例模式,obj就是obj2,所以都打印的he,he
四、__del__
用于在对象被垃圾回收之前执行一些清理工作。当没有任何变量引用一个对象时,该对象就会被垃圾回收机制回收,此时就会自动调用__del__
方法。在 Python 中,当使用 del
关键字删除一个对象时,如果该对象存在其他引用,则只会删除这个引用,而不是直接删除该对象本身。如果该对象不存在其他引用,则会立即将其从内存中删除(Python 解释器会自动调用垃圾回收机制来清除该对象的内存)。
在以下情况,__del__会被调用:
- 对象的引用计数变为0时,即没有任何变量或其他数据结构引用该对象时。
- 程序正常退出时,一些未引用的对象可能会被系统自动回收。
- 使用
del
关键字显式地删除对象时。例子:
只有真正删除才调用__del__
class MyClass: def __init__(self, name): self.name = name print("Creating object:", self.name) def __del__(self): print("Deleting object:", self.name) obj1 = MyClass("Object 1") obj2 = MyClass("Object 2") obj3 = obj1 del obj1 # 这时obj1还存在引用,__del__不会调用 del obj2 print("Deleting obj3") del obj3 """ 打印结果: Creating object: Object 1 Creating object: Object 2 Deleting object: Object 2 Deleting obj3 Deleting object: Object 1 """
python垃圾回收策略:
当我们使用
del
关键字删除一个对象的所有引用时,该对象并不会立即被删除,而是等待垃圾回收。Python的垃圾回收机制采用的是引用计数和分代回收相结合的方式,能够自动处理内存管理。
什么是分代回收?
Python使用了基于引用计数的垃圾回收机制,但是这种方式不能解决循环引用的问题。为了解决这个问题,Python还使用了分代回收机制,即将对象根据其存活时间分为不同的代,并针对各个代采用不同的垃圾回收策略。Python中默认将对象分为三代,分别是0代、1代和2代,每一代都有自己的垃圾回收频率和算法(新创建的对象属于第0代,而第1代和第2代包含了生存时间较长的对象。垃圾回收器会更频繁地检查第0代对象,而对第1代和第2代的检查则较少)。通过这种机制,Python可以更加高效地管理内存空间。
注意点:
在实际开发中,应该尽量避免使用__del__
方法,因为它的使用可能会导致一些难以调试的问题。如果需要在对象被销毁时执行一些清理工作,可以考虑使用上下文管理器或者使用atexit
模块注册一个回调函数。