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类参数self使用示例
Feb 17 Python
Python创建系统目录的方法
Mar 11 Python
Python UnicodeEncodeError: 'gbk' codec can't encode character 解决方法
Apr 24 Python
Mac 上切换Python多版本
Jun 17 Python
pycharm下查看python的变量类型和变量内容的方法
Jun 26 Python
基于数据归一化以及Python实现方式
Jul 11 Python
python实现多张图片拼接成大图
Jan 15 Python
使用python制作一个为hex文件增加版本号的脚本实例
Jun 12 Python
在python中使用pyspark读写Hive数据操作
Jun 06 Python
python实现图书馆抢座(自动预约)功能的示例代码
Sep 29 Python
最新版 Windows10上安装Python 3.8.5的步骤详解
Nov 28 Python
pytorch 6 batch_train 批训练操作
May 28 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 强制下载文件代码
2010/10/24 PHP
深入解析PHP垃圾回收机制对内存泄露的处理
2013/06/14 PHP
php过滤html标记属性类用法实例
2014/09/23 PHP
PHP_SELF,SCRIPT_NAME,REQUEST_URI区别
2014/12/24 PHP
php实现在站点里面添加邮件发送的功能
2020/04/28 PHP
TP5(thinkPHP5框架)实现显示错误信息及行号功能的方法
2019/06/03 PHP
PHP使用PDO实现mysql防注入功能详解
2019/12/20 PHP
js操作Xml(向服务器发送Xml,处理服务器返回的Xml)(IE下有效)
2009/01/30 Javascript
替代window.event.srcElement效果的可兼容性的函数
2009/12/18 Javascript
IE6不能修改NAME问题的解决方法
2010/09/03 Javascript
基于jquery的从一个页面跳转到另一个页面的指定位置的实现代码(带平滑移动的效果)
2011/05/24 Javascript
js实现带搜索功能的下拉框实时搜索实时匹配
2013/11/05 Javascript
学习JavaScript编程语言的8张思维导图分享
2015/03/27 Javascript
JS实现的N多简单无缝滚动代码(包含图文效果)
2015/11/06 Javascript
jQuery代码实现表格中点击相应行变色功能
2016/05/09 Javascript
利用node.js写一个爬取知乎妹纸图的小爬虫
2017/05/03 Javascript
详解React Native开源时间日期选择器组件(react-native-datetime)
2017/09/13 Javascript
用Electron写个带界面的nodejs爬虫的实现方法
2019/01/29 NodeJs
vue踩坑记录之数组定义和赋值问题
2019/03/20 Javascript
JS工厂模式开发实践案例分析
2019/10/17 Javascript
Python中Collection的使用小技巧
2014/08/18 Python
python如何为创建大量实例节省内存
2018/03/20 Python
python使用knn实现特征向量分类
2018/12/26 Python
python的命名规则知识点总结
2019/10/04 Python
简单了解python中的f.b.u.r函数
2019/11/02 Python
pycharm无法安装第三方库的问题及解决方法以scrapy为例(图解)
2020/05/09 Python
使用Html5实现异步上传文件,支持跨域,带有上传进度条
2016/09/17 HTML / CSS
保时捷设计:Porsche Design
2019/03/30 全球购物
英国和世界各地预订便宜的酒店:LateRooms.com
2019/05/05 全球购物
自我鉴定 电子商务专业
2014/01/30 职场文书
促销活动总结
2014/04/28 职场文书
学雷锋献爱心倡议书
2015/04/27 职场文书
小学课改工作总结
2015/08/13 职场文书
《从现在开始》教学反思
2016/02/16 职场文书
python缺失值的解决方法总结
2021/06/09 Python
flex弹性布局详解
2022/03/20 HTML / CSS