python---构造函数、析构函数

文章目录

构造函数

在 Python 中,构造函数指的是一个特殊的实例方法,名为 :

bash 复制代码
__init__ #(注意,前后都是两个下划线)。

构造函数的作用

1、初始化新创建的对象:当一个类的实例被创建后(即内存空间被分配之后),init 方法会立即被自动调用。

2、为对象的属性设置初始值:这是它最主要的工作。你可以通过参数将初始值传递给 init 方法,然后将其赋给对象的属性(即 self.attribute)。

基本语法和示例

bash 复制代码
class ClassName:
    def __init__(self, param1, param2, ...):
        # 初始化代码
        self.attribute1 = param1
        self.attribute2 = param2
        # ... 其他初始化操作

self:这是第一个且必不可少的参数。它代表当前对象的实例(也就是新创建的那个对象本身)。通过 self,你可以在类内部访问该实例的属性和方法。在调用方法时,你不需要手动传递这个参数,Python 会自动处理。

其他参数:你可以根据需要定义任意数量的其他参数。在创建对象时,必须提供这些参数(除非它们有默认值)。

一个简单的 Person 类

bash 复制代码
class Person:
    # 构造函数,接受 name 和 age 两个参数
    def __init__(self, name, age):
        print("一个Person对象被创建了!")
        self.name = name  # 将参数name的值赋给实例的name属性
        self.age = age    # 将参数age的值赋给实例的age属性

    def introduce(self):
        print(f"大家好,我叫{self.name},今年{self.age}岁。")

# 创建对象实例
# 这会自动调用 __init__ 方法,并将 "Alice" 和 30 传递给它
person1 = Person("Alice", 30)

# 访问对象的属性
print(person1.name) # 输出: Alice
print(person1.age)  # 输出: 30

# 调用对象的方法
person1.introduce() # 输出: 大家好,我叫Alice,今年30岁。

带默认参数的构造函数

参数设置默认值,这使得它们在创建对象时成为可选的。

使用默认参数:

bash 复制代码
class Person:
    # age 参数有一个默认值 18
    def __init__(self, name, age=18):
        self.name = name
        self.age = age

# 不提供age,使用默认值
person_young = Person("Bob")
print(person_young.name, person_young.age) # 输出: Bob 18

# 提供age,覆盖默认值
person_old = Person("Charlie", 65)
print(person_old.name, person_old.age) # 输出: Charlie 65

new 方法的区别

需要特别注意的是,init 并不是真正意义上的"构造"函数。在 Python 中,真正负责创建(分配内存)对象的是另一个特殊方法 new

new:这是一个类方法(尽管不用 @classmethod 装饰器),它负责创建并返回一个新的对象实例。它是真正意义上的"构造函数"。

init :它是在 new 完成创建对象之后被调用的,负责初始化这个新创建的对象(给对象的属性赋值)。

在绝大多数情况下,你不需要重写 new 方法,使用默认的即可。init 才是你用来初始化对象的地方。

执行顺序: new -> init

析构函数

析构函数(Destructor)是面向对象编程中的一个特殊方法,它的作用与构造函数相反。当一个对象被销毁(例如,其生命周期结束,被从内存中释放)时,析构函数会被自动调用,用于执行一些清理工作,

例如:

1、关闭该对象打开的文件

2、断开网络连接

3、释放非托管资源(如通过 ctypes 调用的 C 库分配的内存)

4、保存最终状态等

在 Python 中,析构函数的方法名是固定的:

bash 复制代码
__del__

析构函数的定义

在类中定义一个名为 del 的方法即可。这个方法不需要任何参数(除了必须的 self),并且没有返回值。

语法:

bash 复制代码
class MyClass:
    def __init__(self, name):
        # 构造函数,初始化对象
        self.name = name
        print(f"对象 {self.name} 被创建了")

    def __del__(self):
        # 析构函数,对象销毁时调用
        print(f"对象 {self.name} 即将被销毁")

3. 析构函数何时被调用?

析构函数的调用是由 Python 的垃圾回收机制(Garbage Collection, GC) 触发的。具体来说,在以下情况下 del 可能会被调用:

1、引用计数降为 0:这是 CPython 解释器主要的垃圾回收机制。当一个对象没有任何变量引用它时,它的引用计数会变为 0,解释器会立即回收它并调用 del

bash 复制代码
def create_obj():
    obj = MyClass("局部对象") # 对象在函数内创建
    # 函数结束时,局部变量 obj 失效,引用计数降为 0

create_obj() # 函数调用结束后,会输出"对象 局部对象 即将被销毁"

2、使用 del 语句:del 语句会删除一个变量名(即减少对象的引用计数),如果引用计数因此变为 0,则会触发析构。

bash 复制代码
obj = MyClass("测试对象")
del obj # 立即输出"对象 测试对象 即将被销毁"

3、程序正常退出时:程序运行结束后,所有对象都会被销毁,它们的 del 方法也会被调用。

重要注意事项和陷阱

a. 调用时机的不确定性

虽然引用计数为 0 时会立即调用 del ,但 Python 还有一种更高级的垃圾回收器来处理循环引用。

循环引用:两个或多个对象相互引用,导致它们的引用计数永远不为 0。

bash 复制代码
class A:
    def __init__(self):
        self.b = None

class B:
    def __init__(self):
        self.a = None

a = A()
b = B()
a.b = b # a 引用 b
b.a = a # b 引用 a,形成循环引用

del a
del b # 即使删除了变量 a 和 b,对象 A 和 B 的引用计数仍为 1(它们相互引用)

对于这种循环引用,引用计数机制无法回收它们。此时,分代垃圾回收器(Generational GC) 会间歇性地运行,检测并清理这些循环引用。这意味着 del 的调用时机变得不确定,你可能无法预测它何时会被执行,甚至可能根本不会被执行(比如解释器异常退出时)。

b. 异常处理

del 方法执行过程中发生的异常不会被捕获,它们会被输出到 sys.stderr,但不会中断程序的执行(如果程序还在运行的话)。

c. 模块全局变量

对于模块级别的全局变量,它们的 del 方法可能在解释器关闭时才被调用。此时,一些该对象所依赖的其他模块或全局变量可能已经被清理或设置为 None(例如 sys.stdout)。如果在 del 中尝试使用这些已被清理的资源,可能会导致异常。

不推荐写法:

bash 复制代码
class BadExample:
    def __del__(self):
        # 在解释器退出时,sys.stdout 可能已经不可用了
        print("Destructor called")

d. 与 try...finally 和 with 语句的对比

非常重要:del 不应该用于管理关键资源(如文件、锁、网络连接)的清理!

Python 提供了更可靠、更可预测的上下文管理器(with 语句)和 try...finally 块来确保资源被正确释放。

使用 with 语句(推荐):

bash 复制代码
with open('file.txt', 'r') as f:
    data = f.read()
# 离开 with 块后,文件 f 会自动、立即地被关闭,无需等待垃圾回收

依赖 del(不推荐):

bash 复制代码
class FileHandler:
    def __init__(self, filename):
        self.file = open(filename, 'r')

    def __del__(self):
        self.file.close() # 不可靠!文件可能很久以后才关闭,或者根本不关闭。

# 使用这个类
handler = FileHandler('file.txt')
data = handler.file.read()
# 文件关闭的时机不确定
相关推荐
hui函数3 分钟前
Flask蓝图:模块化开发的利器
后端·python·flask
一枚小小程序员哈3 分钟前
基于php的萌宠社区网站的设计与实现、基于php的宠物社区论坛的设计与实现
开发语言·php·宠物
跟橙姐学代码3 分钟前
Python 装饰器超详细讲解:从“看不懂”到“会使用”,一篇吃透
前端·python·ipython
站大爷IP6 分钟前
Rust爬虫实战:用reqwest+select打造高效网页抓取工具
python
Chandler_Song23 分钟前
Excel 转化成JSON
python·json
山烛43 分钟前
深度学习:CUDA、PyTorch下载安装
人工智能·pytorch·python·深度学习·cuda
老赵的博客1 小时前
QT的项目pro qmake编译
开发语言·qt
枯萎穿心攻击2 小时前
从 Unity UGUI 到 Unreal UMG 的交互与高效实践:UI 事件、坐标系适配与性能优化
开发语言·ui·unity·性能优化·ue5·游戏引擎·虚幻引擎
WALL-EC2 小时前
Qt工具栏中图标槽函数没有响应的问题分析
开发语言·qt·osgearth
WCL-JAVA2 小时前
java生成带水印的pdf文件
java·python·pdf