当前位置: 首页 > news >正文

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

文章目录

  • 构造函数
    • 构造函数的作用
    • 基本语法和示例
      • 一个简单的 Person 类
      • 带默认参数的构造函数
    • 与 __new__ 方法的区别
  • 析构函数
    • 析构函数的定义
    • 3. 析构函数何时被调用?
    • 重要注意事项和陷阱
      • a. 调用时机的不确定性
      • b. 异常处理
      • c. 模块全局变量
      • d. 与 try...finally 和 with 语句的对比

构造函数

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

__init__ #(注意,前后都是两个下划线)。

构造函数的作用

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

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

基本语法和示例

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

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

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

一个简单的 Person 类

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岁。

带默认参数的构造函数

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

class Person:# age 参数有一个默认值 18def __init__(self, name, age=18):self.name = nameself.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 中,析构函数的方法名是固定的:

__del__

析构函数的定义

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

class MyClass:def __init__(self, name):# 构造函数,初始化对象self.name = nameprint(f"对象 {self.name} 被创建了")def __del__(self):# 析构函数,对象销毁时调用print(f"对象 {self.name} 即将被销毁")

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

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

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

def create_obj():obj = MyClass(“局部对象") # 对象在函数内创建# 函数结束时,局部变量 obj 失效,引用计数降为 0create_obj() # 函数调用结束后,会输出“对象 局部对象 即将被销毁”

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

obj = MyClass(“测试对象")
del obj # 立即输出“对象 测试对象 即将被销毁”

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

重要注意事项和陷阱

a. 调用时机的不确定性

虽然引用计数为 0 时会立即调用 del,但 Python 还有一种更高级的垃圾回收器来处理循环引用。
循环引用:两个或多个对象相互引用,导致它们的引用计数永远不为 0。

class A:def __init__(self):self.b = Noneclass B:def __init__(self):self.a = Nonea = 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 中尝试使用这些已被清理的资源,可能会导致异常。
不推荐写法:

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

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

非常重要:del 不应该用于管理关键资源(如文件、锁、网络连接)的清理!
Python 提供了更可靠、更可预测的上下文管理器(with 语句)和 try…finally 块来确保资源被正确释放。
使用 with 语句(推荐):

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

依赖 del(不推荐):

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()
# 文件关闭的时机不确定
http://www.dtcms.com/a/348432.html

相关文章:

  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘gunicorn’问题
  • 【springboot 技术代码】集成mongodb 详细步骤
  • localhost和127.0.0.1的区别
  • 界面规范7-可左右拖动的分割条
  • MATLAB GUI 设计入门:用 Guide 工具快速搭建交互界面
  • React Hooks useEffect的使用
  • React 18+ 并发模式异常
  • Linux服务测试题(DNS,NFS,DHCP,HTTP)
  • pytorch线性回归(二)
  • ⭐CVPR2025 病理分析全能模型 CPath-Omni 横空出世
  • RAG智能问答为什么需要进行Rerank?
  • 春秋云镜 Flarum
  • UCIE Specification详解(二)
  • Linux学习-TCP网络协议
  • 基于springboot的高校后勤保修服务系统/基于android的高校后勤保修服务系统app
  • openFeign用的什么协议,dubbo用的什么协议
  • 【重学MySQL】八十七. 触发器管理全攻略:SHOW TRIGGERS与DROP TRIGGER实战详解
  • k8s下的网络通信之calico与调度
  • MySQL官方C/C++ 接口入门
  • 从栈到堆:深入理解C语言静态与动态链表的创建与管理
  • 利旧小天才儿童电话手表实现“一键寻车”功能
  • 线程整理文档
  • 使用UE5开发《红色警戒3》类战略养成游戏的硬件配置指南
  • 【Spring Cloud 微服务】3.智能路由器——深入理解与配置负载均衡
  • MySQL的更新语句执行过程涉及了哪些文件的写入,衍生了redo、undo、二进制日志在什么时候进行写入
  • 从 JUnit 深入理解 Java 注解与反射机制
  • HarmonyOS NEXT系列之元服务框架ASCF
  • 波兰密码破译机bomba:二战密码战的隐形功臣
  • 深入OpenHarmony OTA硬核升级
  • ComfyUI ZLUDA AMD conda 使用遇到的问题