详解Python中的__init__和__new__


Posted in Python onMarch 12, 2014

一、__init__ 方法是什么?
使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候。例如:

# -*- coding: utf-8 -*-
class Person(object):
    """Silly Person"""
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)
if __name__ == '__main__':
    piglei = Person('piglei', 24)
    print piglei

这样便是__init__最普通的用法了。但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。
二、__new__ 方法是什么?
__new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
# -*- coding: utf-8 -*-
class Person(object):
    """Silly Person"""
    def __new__(cls, name, age):
        print '__new__ called.'
        return super(Person, cls).__new__(cls, name, age)
    def __init__(self, name, age):
        print '__init__ called.'
        self.name = name
        self.age = age
    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)
if __name__ == '__main__':
    piglei = Person('piglei', 24)
    print piglei

执行结果:
piglei@macbook-pro:blog$ python new_and_init.py
__new__ called.
__init__ called.
<Person: piglei(24)>

通过运行这段代码,我们可以看到,__new__方法的调用是发生在__init__之前的。其实当 你实例化一个类的时候,具体的执行逻辑是这样的:

1.p = Person(name, age)
2.首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会 返回Person类的一个实例(通常情况下是使用 super(Persion, cls).__new__(cls, ... ...) 这样的方式),
3.然后利用这个实例来调用类的__init__方法,上一步里面__new__产生的实例也就是 __init__里面的的 self
所以,__init__ 和 __new__ 最主要的区别在于:
1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。
但是说了这么多,__new__最通常的用法是什么呢,我们什么时候需要__new__?
三、__new__ 的作用
依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。
首先我们来看一下第一个功能,具体我们可以用int来作为一个例子:
假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能会写出这样的代码。

class PositiveInteger(int):
    def __init__(self, value):
        super(PositiveInteger, self).__init__(self, abs(value))
i = PositiveInteger(-3)
print i

但运行后会发现,结果根本不是我们想的那样,我们任然得到了-3。这是因为对于int这种 不可变的对象,我们只有重载它的__new__方法才能起到自定义的作用。
这是修改后的代码:
class PositiveInteger(int):
    def __new__(cls, value):
        return super(PositiveInteger, cls).__new__(cls, abs(value))
i = PositiveInteger(-3)
print i

通过重载__new__方法,我们实现了需要的功能。
另外一个作用,关于自定义metaclass。其实我最早接触__new__的时候,就是因为需要自定义 metaclass,但鉴于篇幅原因,我们下次再来讲python中的metaclass和__new__的关系。
四、用__new__来实现单例
事实上,当我们理解了__new__方法后,我们还可以利用它来做一些其他有趣的事情,比如实现 设计模式中的 单例模式(singleton) 。
因为类每一次实例化后产生的过程都是通过__new__来控制的,所以通过重载__new__方法,我们 可以很简单的实现单例模式。
class Singleton(object):
    def __new__(cls):
        # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2

输出结果:
value1 value1
True

可以看到obj1和obj2是同一个实例。
Python 相关文章推荐
Python扫描IP段查看指定端口是否开放的方法
Jun 09 Python
python开发之基于thread线程搜索本地文件的方法
Nov 11 Python
python 判断是否为正小数和正整数的实例
Jul 23 Python
Python使用当前时间、随机数产生一个唯一数字的方法
Sep 18 Python
Python2.7下安装Scrapy框架步骤教程
Dec 22 Python
Python爬虫实现百度图片自动下载
Feb 04 Python
python 利用for循环 保存多个图像或者文件的实例
Nov 09 Python
在python2.7中用numpy.reshape 对图像进行切割的方法
Dec 05 Python
深入了解Python枚举类型的相关知识
Jul 09 Python
python getpass模块用法及实例详解
Oct 07 Python
基于Python的自媒体小助手---登录页面的实现代码
Jun 29 Python
python 实用工具状态机transitions
Nov 21 Python
python文件和目录操作方法大全(含实例)
Mar 12 #Python
Python 文件读写操作实例详解
Mar 12 #Python
Python 异常处理实例详解
Mar 12 #Python
Python break语句详解
Mar 11 #Python
Python continue语句用法实例
Mar 11 #Python
Python pass 语句使用示例
Mar 11 #Python
Python自定义函数的创建、调用和函数的参数详解
Mar 11 #Python
You might like
PHP 图片上传实现代码 带详细注释
2010/04/29 PHP
php微信公众开发之获取周边酒店信息的方法
2014/12/22 PHP
PHP实现抓取迅雷VIP账号的方法
2015/07/30 PHP
PHP静态成员变量
2017/02/14 PHP
javascript开发技术大全-第1章javascript概述
2011/07/03 Javascript
jquery事件机制扩展插件 jquery鼠标右键事件
2011/12/21 Javascript
JS 使用for循环遍历子节点查找元素
2014/09/06 Javascript
jQuery插件scroll实现无缝滚动效果
2015/04/27 Javascript
jQuery可见性过滤器:hidden和:visibility用法实例
2015/06/24 Javascript
基于jQuery实现在线选座之高铁版
2015/08/24 Javascript
JS判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个)
2016/08/02 Javascript
js阻止冒泡和默认事件(默认行为)详解
2016/10/20 Javascript
小程序清理本地缓存的方法
2018/08/17 Javascript
el-input 标签中密码的显示和隐藏功能的实例代码
2019/07/19 Javascript
mock.js模拟前后台交互
2019/07/25 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
如何解决jQuery 和其他JS库的冲突
2020/06/22 jQuery
VSCode 配置uni-app的方法
2020/07/11 Javascript
[00:35]可解锁地面特效
2018/12/20 DOTA
初步探究Python程序的执行原理
2015/04/11 Python
Python读写/追加excel文件Demo分享
2018/05/03 Python
详解Django解决ajax跨域访问问题
2018/08/24 Python
如何在Cloud Studio上执行Python代码?
2019/08/09 Python
Python3变量与基本数据类型用法实例分析
2020/02/14 Python
django项目中新增app的2种实现方法
2020/04/01 Python
澳大利亚香水在线:Price Rite Mart
2017/12/28 全球购物
英国最大的在线奢侈手表零售商:Jura Watches
2018/01/29 全球购物
木马的传播途径主要有哪些
2016/04/08 面试题
《夸父追日》教学反思
2014/02/26 职场文书
妇联领导班子剖析材料
2014/08/21 职场文书
天猫活动策划方案
2014/08/21 职场文书
企业投资意向书
2015/05/09 职场文书
嘉年华活动新闻稿
2015/07/17 职场文书
2019行政前台转正申请书范文3篇
2019/08/15 职场文书
python实现简单的井字棋
2021/05/26 Python
动漫APP软件排行榜前十名,半次元上榜,第一款由腾讯公司推出
2022/03/18 杂记