Python类方法__init__和__del__构造、析构过程分析


Posted in Python onMarch 06, 2015

最近学习《Python参考手册》学到Class部分,遇到了类的构造析构部分的问题:

1、什么时候构造?
2、什么时候析构?
3、成员变量如何处理?
4、Python中的共享成员函数如何访问?
------------------------
探索过程:
1、经过查找,Python中没有专用的构造和析构函数,但是一般可以在__init__和__del__分别完成初始化和删除操作,可用这个替代构造和析构。还有一个__new__用来定制类的创建过程,不过需要一定的配置,此处不做讨论。
2、类的成员函数默认都相当于是public的,但是默认开头为__的为私有变量,虽然是私有,但是我们还可以通过一定的手段访问到,即Python不存在真正的私有变量。如:

__priValue = 0 # 会自动变形为"_类名__priValue"的成员变量

3、由于Python的特殊性,全局成员变量是共享的,所以类的实例不会为它专门分配内容空间,类似于static,具体使用参看下面的例子。

测试1:

# encoding:utf8
class NewClass(object):

    num_count = 0 # 所有的实例都共享此变量,即不单独为每个实例分配

    def __init__(self,name):

        self.name = name

        NewClass.num_count += 1

        print name,NewClass.num_count

    def __del__(self):

        NewClass.num_count -= 1

        print "Del",self.name,NewClass.num_count

    def test():

        print "aa"
aa = NewClass("Hello")

bb = NewClass("World")

cc = NewClass("aaaa")
print "Over"

调试运行:

Hello 1

World 2

aaaa 3

Over

DeException l Hello 2

AttributeError: "'NoneType' object has no attribute 'num_count'" in <bound method NewClass.__del__ of <__main__.NewClass object at 0x01AF18D0>> ignored

Exception AttributeError: "'NoneType' object has no attribute 'num_count'" in <bound method NewClass.__del__ of <__main__.NewClass object at 0x01AF1970>> ignored

我们发现,num_count 是全局的,当每创建一个实例,__init__()被调用,num_count 的值增一,当程序结束后,所有的实例会被析构,即调用__del__() 但是此时引发了异常。查看异常为 “NoneType” 即 析构时NewClass 已经被垃圾回收,所以会产生这样的异常。

但是,疑问来了?为什么会这样?按照C/C++等语言的经验,不应该这样啊!经过查找资料,发现:

Python的垃圾回收过程与常用语言的不一样,Python按照字典顺序进行垃圾回收,而不是按照创建顺序进行。所以当系统进行回收资源时,会按照类名A-Za-z的顺序,依次进行,我们无法掌控这里的流程。

明白这些,我们做如下尝试:

# encoding:utf8
class NewClass(object):

    num_count = 0 # 所有的实例都共享此变量,即不单独为每个实例分配

    def __init__(self,name):

        self.name = name

        NewClass.num_count += 1

        print name,NewClass.num_count

    def __del__(self):

        NewClass.num_count -= 1

        print "Del",self.name,NewClass.num_count

    def test():

        print "aa"
aa = NewClass("Hello")

bb = NewClass("World")

cc = NewClass("aaaa")
del aa

del bb

del cc
print "Over"

调试输出:

Hello 1

World 2

aaaa 3

Del Hello 2

Del World 1

Del aaaa 0

Over

OK,一切按照我们预料的顺序发生。
但是,我们总不能每次都手动回收吧?这么做Python自己的垃圾回收还有什么意义?

SO,继续查找,我们还可以通过self.__class__访问到类本身,然后再访问自身的共享成员变量,即 self.__class__.num_count , 将类中的NewClass.num_count替换为self.__class__.num_count 编译运行,如下:

# encoding:utf8
class NewClass(object):

    num_count = 0 # 所有的实例都共享此变量,即不单独为每个实例分配

    def __init__(self,name):

        self.name = name

        self.__class__.num_count += 1

        print name,NewClass.num_count

    def __del__(self):

        self.__class__.num_count -= 1

        print "Del",self.name,self.__class__.num_count

    def test():

        print "aa"
aa = NewClass("Hello")

bb = NewClass("World")

cc = NewClass("aaaa")
print "Over"

结果:

Hello 1

World 2

aaaa 3

Over

Del Hello 2

Del World 1

Del aaaa 0

Perfect!我们完美地处理了这个问题!

PS:

书上又提到了一些问题,在这里作补充(仅作为参考):

__new__()是唯一在实例创建之前执行的方法,一般用在定义元类时使用。

del xxx 不会主动调用__del__方法,只有引用计数==0时,__del__()才会被执行,并且定义了__del_()的实例无法被Python的循环垃圾收集器收集,所以尽量不要自定义__del__()。一般情况下,__del__() 不会破坏垃圾处理器。

实验中发现垃圾回收自动调用了__del__, 这与书上所说又不符,不知是什么原因,需要继续学习。

Python 相关文章推荐
Python常用小技巧总结
Jun 01 Python
python实现获取Ip归属地等信息
Aug 27 Python
Python 多进程和数据传递的理解
Oct 09 Python
Python 3.7新功能之dataclass装饰器详解
Apr 21 Python
详解windows python3.7安装numpy问题的解决方法
Aug 13 Python
Python面向对象思想与应用入门教程【类与对象】
Apr 12 Python
python 模拟贷款卡号生成规则过程解析
Aug 30 Python
利用python计算时间差(返回天数)
Sep 07 Python
Python定义函数时参数有默认值问题解决
Dec 19 Python
python GUI库图形界面开发之PyQt5 MDI(多文档窗口)QMidArea详细使用方法与实例
Mar 05 Python
python Autopep8实现按PEP8风格自动排版Python代码
Mar 02 Python
Python爬虫爬取全球疫情数据并存储到mysql数据库的步骤
Mar 29 Python
Python列表生成器的循环技巧分享
Mar 06 #Python
Python装饰器使用示例及实际应用例子
Mar 06 #Python
Python迭代器和生成器介绍
Mar 06 #Python
Python __setattr__、 __getattr__、 __delattr__、__call__用法示例
Mar 06 #Python
Python比较文件夹比另一同名文件夹多出的文件并复制出来的方法
Mar 05 #Python
Python挑选文件夹里宽大于300图片的方法
Mar 05 #Python
python基于windows平台锁定键盘输入的方法
Mar 05 #Python
You might like
PHP无刷新上传文件实现代码
2011/09/19 PHP
PHP随手笔记整理之PHP脚本和JAVA连接mysql数据库
2015/11/25 PHP
基于PHP技术开发客服工单系统
2016/01/06 PHP
firefox浏览器下javascript 拖动层效果与原理分析代码
2007/12/04 Javascript
兼容多浏览器的iframe自适应高度(ie8 、谷歌浏览器4.0和 firefox3.5.3)
2009/11/04 Javascript
jquery $.each() 使用小探
2013/08/23 Javascript
jquery事件与函数的使用介绍
2013/09/29 Javascript
JavaScript设计模式之外观模式介绍
2014/12/28 Javascript
JavaScript 经典实例日常收集整理(常用经典)
2016/03/30 Javascript
JavaScript递归操作实例浅析
2016/10/31 Javascript
AngularJS中一般函数参数传递用法分析
2016/11/22 Javascript
实例讲解javascript实现异步图片上传方法
2017/12/05 Javascript
JS实现的小火箭发射动画效果示例
2018/12/08 Javascript
JS拖拽排序插件Sortable.js用法实例分析
2019/02/20 Javascript
vue + axios get下载文件功能
2019/09/25 Javascript
使用p5.js临摹动态图片
2019/11/04 Javascript
[03:10]超级美酒第四天 fy拉比克秀 大合集
2018/06/05 DOTA
[51:28]EG vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/16 DOTA
python封装对象实现时间效果
2020/04/23 Python
python静态方法实例
2015/01/14 Python
Python实现比较两个列表(list)范围
2015/06/12 Python
tensorflow实现softma识别MNIST
2018/03/12 Python
Python常见字典内建函数用法示例
2018/05/14 Python
解决Pycharm出现的部分快捷键无效问题
2018/10/22 Python
中国综合网上购物商城:苏宁易购
2016/08/09 全球购物
美国一家著名的手表在线折扣网站:Discount Watch Store
2020/02/24 全球购物
J2SDK1.5与J2SDK5.0有什么区别
2012/09/19 面试题
Delphi工程师笔试题
2013/09/21 面试题
销售简历自我评价
2014/01/24 职场文书
前台文员职责范本
2014/03/07 职场文书
安全标语大全
2014/06/10 职场文书
新闻发布会活动策划方案
2014/09/15 职场文书
财务会计岗位职责
2015/02/03 职场文书
护士求职自荐信范文
2015/03/04 职场文书
盗窃罪辩护词范文
2015/05/21 职场文书
高并发下Redis如何保持数据一致性(避免读后写)
2022/03/18 Redis