27、Python之面向对象:方生方死?对象生命周期是如何管理的

news2024/11/22 21:15:33

引言

前面关于面向对象的几篇文章,其实主要围绕着面向对象的第一个核心理念——封装,进行面向对象的介绍。从类、对象的静态构成的角度,对类与对象的定义及使用进行介绍。

在进入面向对象另外两个理念的介绍之前,我觉得有必要对Python中对象的生命周期管理进行一些介绍,从而知道我们通过代码定义了类对象、实例对象,这些对象是怎么创建出来的,又是怎么被销毁的。从动态的视角对对象的定义及使用进行一个补充说明。

再看类对象

在探讨实例对象的生命周期之前,有必要先回顾一下关于类也是对象的概念。

Python中一切皆对象,类也是对象。

我们以一个简单的类定义示例,查看类对象的特性与实例对象的关联关系:

import sys


class DaGongRen:
    def __init__(self, name):
        self.name = name


if __name__ == '__main__':
    # 类对象的输出
    print(DaGongRen)
    print(id(DaGongRen))
    print(type(DaGongRen))
    if isinstance(DaGongRen, type):
        print('True')
    # 输出当前的类对象的引用计数,先不要管这个引用计数的初始值
    print(sys.getrefcount(DaGongRen))
    # 实例化该类的第一个实例对象
    zs = DaGongRen('张三')
    # 可以看到类对象的引用计数+1
    print(sys.getrefcount(DaGongRen))
    # 与id(DaGongRen)输出相同
    print(id(zs.__class__))
    # 与id(DaGongRen)输出相同,type类返回对象所属的类对象
    print(id(type(zs)))
    # 实例化该类的第二个实例对象
    ls = DaGongRen('李四')
    # 与id(DaGongRen)输出相同
    print(id(ls.__class__))
    # 可以看到类对象的引用计数+1
    print(sys.getrefcount(DaGongRen))
    del zs
    # 可以看到类对象的引用计数-1
    print(sys.getrefcount(DaGongRen))


执行结果:

76c92edc6d5fd1fa87d170bb23b5e464.jpeg

从上面的执行结果中,我们可以得出这样的一些结论:

1、类也是对象,有对应的id,type()返回type,表示类对象是由type类进行实例化得来的对象。

2、实例化对象中有一个属性__class__存储该对象所属类对象的引用;当然我们也可以通过DaGongRen.__class__获取类对象所属类对象的引用,会得到<class 'type'>。

3、每实例化一个实例化对象,都会增加一个对类对象的引用,引用计数+1,每销毁一个实例化对象,则会减少一个类对象的引用。

4、类对象是一种单例模式的存在,通过type(obj)返回的类对象、通过obj.__class__获取的类对象,或者通过类名本身引用的类对象,id都是相同的,所以都是同一个类对象。

通过上面这些结论,我们也能大概知道为什么对象能够访问到类属性、类方法,因为实例化对象持有类对象的引用,也就是__class__属性。

实例对象的生命周期

类对象相对特殊一些,但是,从上面的示例也能看出来,类对象也都是type类的实例化对象。接下来,我们对DaGongRen类的定义进行扩充,来看实例对象的生命周期管理。

首先,简单补充一下Python垃圾回收的相关内容:

1、最朴素的垃圾回收原理,是基于引用计数的方式来进行的,如果一个对象没有被引用了,也就是引用计数为0,一定是不可用的,所以该对象会被标记为可回收,在恰当的时机会被进行垃圾回收,从而释放对象所占用的内存。

2、在CPython中,垃圾回收使用的主要算法是引用计数。实际上,每个对象都会统计有多少引用指向自己。当引用计数归零时,对象会被销毁:CPython会首先在对象上调用__del__方法(如果定义了),然后释放分配给对象的内存。

3、引用计数是存在缺陷的,如果存在两个对象的循环引用,则会始终导致无法进行垃圾回收。

4、CPython2.0增加了分代垃圾回收算法,用于检测引用循环中涉及的对象组——如果一组对象之间全是相互引用,那么即使再出色的引用方式也会导致组中的对象不可达。

直接通过代码实例来看:

class DaGongRen:
    # 对象实例化的魔法函数,或者钩子函数,会被自动调用
    # 一般不要重写
    def __new__(cls, *args, **kwargs):
        self = super().__new__(cls)
        print(f"实例化方法:__new__被调用,分配内存,实例化对象")
        return self

    # 对实例化对象进行初始化操作,主要是定义属性并赋予默认值
    def __init__(self, name):
        self.name = name
        print(f"初始化方法:__init__被调用,进行对象属性初始化")

    # 实例化对象被垃圾回收之前,会调用该方法,有时也称为析构方法
    # 可以进行一些资源释放、关闭的相关操作,一般不要重写
    def __del__(self):
        print(f"析构方法:__del__被调用,对象{id(self)}将被销毁,并释放内存")


if __name__ == '__main__':
    zs = DaGongRen('张三')

执行结果:

6c4e1e5b12688c94e379a476600d94d4.jpeg

通过代码示例,可以有如下结论:

1、Python中的对象的生命周期主要有三个阶段:对象实例化、对象初始化、对象被销毁。

2、生命周期的三个阶段,分别会对应3个钩子方法:__new__、__init__、__del__。

3、钩子方法通常不需要我们自己手动调用,Python解释器会根据对象实例化操作或者垃圾回收自动调用对应的方法。

a8a2ff5dbefe7b55a1cc016cae390ae9.jpeg

上面的代码只是用于进行对象生命周期的演示,在实际应用中,需要注意的是:

1、类的__new__() 方法很少通过用户代码定义。如果定义了它,它通常是用原型__new__(cls, *args, **kwargs) 编写的,其中args 和kwargs 与传递给__init__() 的参数相同。__new__() 始终是一个类方法,接受类对象作为第一个参数。尽管__new__() 会创建一个实例,但它不会自动调用__init__() 。

2、如果在类中定义了__new__() ,通常表明这个类会做两件事之一。首先,该类可能继承自一个基类,该基类的实例是不可变的。如果定义的对象继承自不可变的内置类型(如整数、字符串或元组),常常会遇到这种情况,因为__new__() 是唯一在创建实例之前执行的方法,也是唯一可以修改值的地方(也可以在__init__() 中修改,但这时修改可能为时已晚)。

3、创建实例之后,实例将由引用计数来管理。如果引用计数到达0,实例将立即被销毁。当实例即将被销毁时,解释器首先会查找与对象相关联的__del__() 方法并调用它。而实际上,很少有必要为类定义__del__() 方法。唯一的例外是在销毁对象之后需要执行清除操作(如关闭文件、关闭网络连接或释放其他系统资源)。即使在这种情况下,依靠__del__() 来完全关闭实例也存在一定的危险,因为无法保证在解释器退出时会调用该方法。更好的方案是定义一个方法,如close() ,程序可以使用该方法显式执行关闭操作。

4、对象绝不会自行销毁,然而,当对象不可达时,可能会被当做垃圾回收。而所谓的垃圾回收最根本的一个保证,不是一定回收垃圾,而是不要把非垃圾对象进行错误回收。

总结

今天的文章中,首先回顾了Python中类也是对象的概念,然后粗略介绍了Python中对象的生命周期管理。通过这些内容的介绍,可以对Python中一个对象从创建到销毁的全过程,有个整体上的认知,从而为进行Python内容更深入的学习打下基础。

感谢您拨冗阅读,如果能对您稍微有一点点帮助,那就是本文的最大价值了。

4ff616310b93ba565d27235e488689f2.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1970172.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回调函数和qsort,strcmp函数

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 1&#xff0e;回调函数是什么&#xff1f; 回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向…

【LLM大模型】GraphRAG入门学习流程

GraphRAG GraphRAG 是一种基于图的检索增强方法&#xff0c;由微软开发并开源。它通过结合LLM和图机器学习的技术&#xff0c;从非结构化的文本中提取结构化的数据&#xff0c;构建知识图谱&#xff0c;以支持问答、摘要等多种应用场景。GraphRAG的特色在于利用图机器学习算法…

7、springboot3 vue3开发平台-后端-获取用户菜单,构建菜单树列表

1. 获取用户信息并 从用户session 中获取用户信息 Operation(summary "查询当前用户菜单")GetMapping("/getSelfMenu")public Result<List<RouterVO>> getSelfMenu() {UserInfo userLoginInfo (UserInfo) StpUtil.getSession().get("u…

【Docker系列】Docker 中-d 和-it 的区别

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python开发: 飞机大战 小游戏

玩法 你可以控制飞机左右移动,躲避敌机子弹,同时发射自己的炮弹,将敌人击落! 部署方案: 1、代码如下图; 2、将代码保存到一个python中,比如planeFight.py; 3、在你的电脑中安装python环境,然后使用命令:“py planeFight.py” 运行这个文件即可; 代码 import p…

大模型深度神经网络(Deep Neural Network, DNN)

大模型深度神经网络&#xff08;Deep Neural Network, DNN&#xff09;是一种复杂的机器学习模型&#xff0c;其特点在于包含多个隐藏层&#xff0c;从而赋予模型强大的非线性表达能力和对复杂数据模式的学习能力。以下是对大模型DNN的详细介绍&#xff1a; 一、基本概念 深度…

机器学习之贝叶斯方法

机器学习之贝叶斯方法 1. 贝叶斯定理基础1.1 贝叶斯定理公式1.2 先验概率 (Prior Probability)1.3 后验概率 (Posterior Probability)1.4 似然 (Likelihood)1.5 证据 (Evidence)1.6 贝叶斯定理的应用实例 2. 贝叶斯方法的基本概念2.1 条件概率 (Conditional Probability)2.2 全…

为什么康耐视visionpro的C#二次开发调用的recorddisplay控件偶尔会显示白色的,偶尔又正常了?

recorddisplay控件正常显示 异常显示 原因分析&#xff1a; 没有完全加载recorddisplay控件&#xff0c;有可能是有bug没有完全加载&#xff0c;打断点调试控件是否完全加载。

8、springboot3 vue3开发平台-后端-使用aop 添加系统访问日志

1. 添加依赖&#xff0c; 创建数据库 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- IP地址解析 --><dependency><groupId>org.lionsou…

小程序~~3(自定义组件)

目录 1.自定义组件 创建和注册组件 数据和方法 属性properties 组件wxml的slot-插槽&#xff08;难&#xff09; 组件样式以及注意事项&#xff08;难&#xff09; 组件样式隔离&#xff08;难&#xff09; 修改checkbox样式&#xff08;难&#xff09; 数据监听函数o…

Taming Lookup Tables for Efficient Image Retouching

Abstract 高清屏幕在终端用户相机、智能手机和电视等边缘设备中的广泛使用&#xff0c;刺激了对图像增强的巨大需求。现有的增强模型通常针对高性能进行优化&#xff0c;但不能减少硬件推断时间和功耗&#xff0c;尤其是在计算和存储资源受限的边缘设备上。为此&#xff0c;我…

等保2.0与安全编程:携手共筑网络安全防线

一、基本概念 等保2.0&#xff08;网络安全等级保护2.0&#xff09;&#xff1a;作为我国网络安全领域的基本国策和制度&#xff0c;等保2.0是对原有信息安全等级保护&#xff08;等保1.0&#xff09;的全面升级。它于2019年正式实施&#xff0c;旨在应对云计算、大数据、物联网…

基于SSM的停车场管理系统 毕业设计-附源码42934

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3研究方案 1.4论文章节安排 2相关技术介绍 2.1 B/S结构 2.2 SSM框架 2.3 Java语言 2.4 MySQL数据库 3系统分析 3.1 可行性分析 3.2 系统功能性分析 3.3.非功能性分析 3.4 系统用例分析 3.5系统流程分析 3.5.1…

JAVA基础 - 反射

目录 一. 简介 二. java.lang.Class类 三. java.lang.reflect包 四. 创建对象 五. 调用方法 六. 调用成员变量 一. 简介 反射是 Java 语言中的一种强大机制&#xff0c;允许程序在运行时动态地获取类的信息、访问类的成员&#xff08;包括字段、方法和构造函数&#xff…

fal.ai发布超分辨率模型——AuraSR V2

今天&#xff0c;我们发布了单步 GAN 升频器的第二个版本&#xff1a; AuraSR。 我们在上个月发布了 AuraSR v1&#xff0c;社区的反响让我们深受鼓舞&#xff0c;因此我们立即开始了新版本的训练。 AuraSR 基于 Adobe Gigagan 论文&#xff0c;以 lucidrain 的实现为起点。Gi…

Off-by-One Error: 编码中的常见陷阱 ⚠️

Off-by-One Error: 编码中的常见陷阱 ⚠️ Off-by-One Error: 编码中的常见陷阱 ⚠️摘要引言正文内容1. 什么是 Off-by-One 错误&#xff1f;Off-by-One 错误的示例 2. 如何识别 Off-by-One 错误&#xff1f;2.1 使用调试器2.2 单元测试 3. 如何预防 Off-by-One 错误&#xff…

Python酷库之旅-第三方库Pandas(059)

目录 一、用法精讲 226、pandas.Series.pad方法 226-1、语法 226-2、参数 226-3、功能 226-4、返回值 226-5、说明 226-6、用法 226-6-1、数据准备 226-6-2、代码示例 226-6-3、结果输出 227、pandas.Series.replace方法 227-1、语法 227-2、参数 227-3、功能 …

【Python机器学习】Logistic回归——从疝气病症预测病马的死亡率

用Logistic回归来预测患有疝病的马的存活问题。这里的数据包括368个样本和28个特征。疝病是描述马肠胃痛的术语&#xff0c;这种病并不一定源自马的肠胃问题。 该数据集中包含了医院检测马疝病的一些指标&#xff0c;有些指标比较主观&#xff0c;有的指标难以测量&#xff0c…

docker部署elasticsearch和Kibana

部署elasticsearch 通过下面的Docker命令即可安装单机版本的elasticsearch&#xff1a; docker run -d \--name es \-e "ES_JAVA_OPTS-Xms512m -Xmx512m" \-e "discovery.typesingle-node" \-v es-data:/usr/share/elasticsearch/data \-v es-plugins:/u…

【STC32G12K128开发板】第3-9讲:手势识别(基于PAJ7620U2)

第3-9讲&#xff1a;手势识别&#xff08;基于PAJ7620U2&#xff09; 学习目的了解IK-PAJ7620U2手势识别传感器模块的功能。掌握IK-PAJ7620U2的I2C协议、操作流程&#xff0c;并编程实现配置IK-PAJ7620U2工作于接近检测和手势识别模式以及读取检测结果。 PAJ7620手势识别模块 产…