在 Python 面向对象编程中,对象的“生”与“死”由三个特殊方法控制:new 负责创建对象,init 负责初始化对象,del 在对象即将被销毁时调用。理解它们的执行顺序和适用场景,是写出健壮 Python 类的基础。
new:对象创建
- 调用时机:在实例化一个类时,最先被调用。它是一个静态方法,用来生成并返回一个新的实例对象。
- 默认行为:如果没有重写,会调用父类(通常是
object)的new,分配内存并返回一个空的实例。 - 什么时候需要重写:
- 单例模式:确保一个类只有一个实例,在
new中控制实例创建。 - 继承不可变类型:比如你想自定义一个
int或tuple的子类,需要在new中创建实例,因为不可变对象在init阶段已经无法修改值。 - 典型代码:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
init:对象初始化
- 调用时机:紧跟在
new返回一个实例之后被调用。new创建了空壳,init负责填充属性。 - 核心作用:对实例进行初始化设置,将传入的参数绑定到实例属性上。这是最常见、最熟悉的构造函数“代言人”。
- 注意事项:
init不能返回任何值(或只能返回None),否则会引发TypeError。- 如果
new返回的不是当前类的实例,则init根本不会被调用。 - 最佳实践:
- 始终在
init中明确所有实例属性,提高代码可读性。 - 复杂初始化逻辑可以单独拆分为私有方法,比如
_initialize,避免init过度臃肿。
del:析构函数
- 调用时机:当对象的引用计数归零,垃圾回收器即将销毁对象时调用。它在对象“临终”前执行一些清理工作。
- 常见用途:
- 关闭文件、网络连接、数据库连接等外部资源。
- 调试时输出对象销毁信息,辅助排查内存泄漏。
- 重大陷阱与避坑指南:
- 不要依赖它做关键清理:
del的调用时机不确定,可能在程序退出时仍未被调用,而且一旦程序崩溃提前终止,del可能根本不会执行。推荐使用上下文管理器(with语句)或显式close()方法来管理资源。 - 避免在
del中访问其他对象:当对象被销毁时,其他对象可能已经消失,容易引发异常。 - 循环引用会推迟
del调用:如果对象间存在循环引用,垃圾回收器需要额外的标记-清除周期才能回收,del的执行会延迟。Python 3.4 之后,循环引用中的del方法在程序退出时仍会被尝试调用,但依然不建议依赖它。 - 典型用法(谨慎使用):
class FileManager:
def __init__(self, filename):
self.file = open(filename, 'w')
def __del__(self):
self.file.close()
更好的做法是用 with 配合 enter/exit。
一个体现三者关系的例子
class Demo:
def __new__(cls, *args, **kwargs):
print("1. __new__ 创建实例")
instance = super().__new__(cls)
return instance
def __init__(self, value):
print("2. __init__ 初始化实例")
self.value = value
def __del__(self):
print("3. __del__ 对象即将被销毁")
# 执行
d = Demo(42)
# 输出:
# 1. __new__ 创建实例
# 2. __init__ 初始化实例
del d # 如果此时没有其他引用,可能会触发 __del__
# 输出: 3. __del__ 对象即将被销毁
实用总结
- 日常开发中 95% 以上的类只需要写
init,不需要碰new和del。 - 需要控制实例创建(如单例、不可变类型子类化)时用
new。 - 资源清理请优先使用上下文管理器(实现
enter/exit),忘掉del是更安全的选择。