self 的本质,知道了对象如何通过方法行为 interact。今天,我们将关注对象的“生与死”——对象是如何被创建的?又是如何被销毁的?__new__ vs __init__很多初学者认为 __init__ 是构造函数,其实不完全准确。Python 中对象的创建分为两步:
__new__:真正的构造函数__init__ 之前执行。cls(类本身)。__init__:初始化方法__new__ 返回实例后执行。self(实例本身)。None(不能有返回值)。obj = MyClass()↓1. __new__(cls) → 创建内存空间,返回实例 obj↓2. __init__(self) → 初始化 obj 的属性↓3. 返回 obj
class LifeCycle:def __new__(cls, *args, **kwargs):print("1. __new__ 被调用:创建实例")instance = super().__new__(cls)return instancedef __init__(self, value):print("2. __init__ 被调用:初始化属性")self.value = valueobj = LifeCycle(100)# 输出:# 1. __new__ 被调用:创建实例# 2. __init__ 被调用:初始化属性
__init__ 初始化方法深度使用通过参数让每个对象拥有不同的初始状态。
class User:def __init__(self, username, email, age=18):self.username = usernameself.email = emailself.age = age # 默认参数u1 = User("alice", "alice@example.com")u2 = User("bob", "bob@example.com", 25)
__init__ 返回值__init__ 不能有返回值,否则会报错。
class Wrong:def __init__(self):return "Error" # ❌ TypeError: __init__ should return Noneclass Correct:def __init__(self):pass # ✅ 默认返回 None
可以在 __init__ 中进行简单的数据验证或资源准备。
class BankAccount:def __init__(self, balance):if balance < 0:raise ValueError("余额不能为负")self.balance = balance
__del__ 与垃圾回收__del__?__del__ 是对象被销毁前调用的方法,称为析构函数。
当对象的引用计数归零,且 Python 垃圾回收机制(GC)启动时。
class Resource:def __init__(self, name):self.name = nameprint(f"{self.name} 创建")def __del__(self):print(f"{self.name} 被销毁")r = Resource("文件对象")del r # 手动删除引用,可能触发 __del__
__del__ 的调用时间不确定,依赖 GC。__del__ 可能永远不会被调用。__del__ 中发生的异常会被忽略,难以调试。__del__ 中访问它们会报错。💡 最佳实践:不要依赖 __del__ 管理关键资源(如文件、数据库连接、网络套接字)。
既然 __del__ 不可靠,如何确保资源被正确释放?
提供 close() 或 cleanup() 方法,让用户手动调用。
class DatabaseConnection:def __init__(self):self.connected = Trueprint("数据库连接已建立")def close(self):if self.connected:self.connected = Falseprint("数据库连接已关闭")db = DatabaseConnection()db.close() # 显式关闭
结合第 22 天学的 with 语句(一起学Python】第22天:函数的返回值 return),实现自动资源管理。
class DatabaseConnection:def __init__(self):self.connected = Falsedef connect(self):self.connected = Trueprint("连接成功")return selfdef close(self):if self.connected:self.connected = Falseprint("连接关闭")def __enter__(self):return self.connect()def __del__(self):# 兜底清理,但不依赖它if self.connected:print("警告:连接未正常关闭")# 使用 with 自动管理with DatabaseConnection() as db:print("正在操作数据库...")# 退出 with 块自动调用 close()
使用 weakref 模块避免循环引用导致的内存泄漏。
__new__)确保一个类只有一个实例。
class Singleton:_instance = Nonedef __new__(cls, *args, **kwargs):if not cls._instance:cls._instance = super().__new__(cls)return cls._instancedef __init__(self, value):self.value = values1 = Singleton(10)s2 = Singleton(20)print(s1 is s2) # True (同一个对象)print(s1.value) # 20 (init 被再次调用,覆盖了值)print(s2.value) # 20
结合类属性与 __init__。
class Person:count = 0 # 类属性def __init__(self, name):self.name = namePerson.count += 1def __del__(self):Person.count -= 1p1 = Person("Alice")p2 = Person("Bob")print(Person.count) # 2del p1# 注意:count 可能不会立即减 1,依赖 GC
__init__ 不能返回值?因为 __init__ 的职责是初始化已经创建的实例,返回非 None 值会干扰 Python 的实例化机制。
__del__ 一定会执行吗?不一定。如果程序异常退出,或存在循环引用,__del__ 可能不被调用。
__init__ 中调用其他方法吗?可以,但要注意避免调用被子类重写的方法,因为此时子类可能还未完全初始化。
通常没有 guaranteed 顺序,尤其是涉及多个对象时。不要在一个对象的 __del__ 中依赖另一个对象的状态。
方法 | 调用时机 | 作用 | 返回值 | 建议 |
|---|---|---|---|---|
| 实例化第一步 | 创建实例 | 必须返回实例 | 特殊场景(单例)使用 |
| 实例化第二步 | 初始化属性 | 必须为 None | 常规初始化使用 |
| 对象销毁前 | 清理资源 | 无 | 不依赖,仅作兜底 |
__init__ 是初始化,__new__ 才是构造。__del__ 管理资源,使用 with 或显式 close()。__init__ 中增加,__del__ 中减少(但需注意 GC 延迟)。创建一个 FileWrapper 类,模拟文件操作。
__init__:接收文件名,打印“打开文件”。__del__:打印“尝试关闭文件”。close():显式关闭,打印“文件已关闭”。with 语句管理它(需实现 __enter__ 和 __exit__,参考第 22 天内容)。class FileWrapper:def __init__(self, filename):self.filename = filenameprint(f"打开文件:{filename}")def close(self):print(f"文件已关闭:{self.filename}")def __enter__(self):return selfdef __exit__(self, exc_type, exc_val, exc_tb):self.close()def __del__(self):print(f"析构:尝试关闭 {self.filename}")# 测试with FileWrapper("test.txt") as f:pass # 自动调用 close# 手动测试f2 = FileWrapper("test2.txt")del f2 # 调用 __del__
使用上面提供的单例模式代码,创建三个实例,验证它们是否是同一个对象,并观察 __init__ 被调用的次数。
# 见案例 1 代码# 结论:s1 is s2 is s3 为 True# __init__ 会被调用 3 次,但 __new__ 只创建 1 次实例
恭喜你!完成了 第 34-38 天 的 OOP 基础学习。你已经掌握了:
明天我们将进入 OOP 核心阶段(第 39-42 天) 的第一天!
super() 函数的作用是什么?💡 提前思考:如果有一个
Animal类,Dog类应该如何继承它?Dog可以拥有Animal没有的方法吗?

掌握生命周期管理,让你的代码更健壮!准备好迎接继承了吗?继续加油!🚀