Python中__init__和__new__的区别详解


Posted in Python onJuly 09, 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中AND、OR的一个使用小技巧
Feb 18 Python
JPype实现在python中调用JAVA的实例
Jul 19 Python
致Python初学者 Anaconda入门使用指南完整版
Apr 05 Python
Python八大常见排序算法定义、实现及时间消耗效率分析
Apr 27 Python
python 实时得到cpu和内存的使用情况方法
Jun 11 Python
Python根据文件名批量转移图片的方法
Oct 21 Python
Python实现字符串匹配的KMP算法
Apr 04 Python
对PyQt5中的菜单栏和工具栏实例详解
Jun 20 Python
Python使用Excel将数据写入多个sheet
May 16 Python
快速解释如何使用pandas的inplace参数的使用
Jul 23 Python
python FTP编程基础入门
Feb 27 Python
PyTorch中permute的使用方法
Apr 26 Python
Python中使用logging模块代替print(logging简明指南)
Jul 09 #Python
Python中的魔法方法深入理解
Jul 09 #Python
gearman的安装启动及python API使用实例
Jul 08 #Python
python实现跨文件全局变量的方法
Jul 07 #Python
Python中的并发编程实例
Jul 07 #Python
Python编程语言的35个与众不同之处(语言特征和使用技巧)
Jul 07 #Python
python基于mysql实现的简单队列以及跨进程锁实例详解
Jul 07 #Python
You might like
wordpress自定义url参数实现路由功能的代码示例
2013/11/28 PHP
PHP动态地创建属性和方法, 对象的复制, 对象的比较,加载指定的文件,自动加载类文件,命名空间
2016/05/06 PHP
PHP生成短网址方法汇总
2016/07/12 PHP
弹出广告特效(一个IP只弹出一次)的代码
2007/07/27 Javascript
js实现的真正的iframe高度自适应(兼容IE,FF,Opera)
2010/03/07 Javascript
不使用浏览器运行javascript代码的方法
2013/07/24 Javascript
深入了解Node.js中的一些特性
2014/09/25 Javascript
基于jQuery仿淘宝产品图片放大镜代码分享
2020/06/23 Javascript
轻松掌握JavaScript中的Math object数学对象
2016/05/26 Javascript
Bootstrap基本组件学习笔记之列表组(11)
2016/12/07 Javascript
vue2里面ref的具体使用方法
2017/10/27 Javascript
jQuery实现的简单无刷新评论功能示例
2017/11/08 jQuery
ExtJs使用自定义插件动态保存表头配置(隐藏或显示)
2018/09/25 Javascript
vue组件暴露和.js文件暴露接口操作
2020/08/11 Javascript
微信小程序自定义yPicker组件实现省市区三级联动功能
2020/10/29 Javascript
[50:11]2018DOTA2亚洲邀请赛 4.7总决赛 LGD vs Mineski 第三场
2018/04/09 DOTA
Python开发虚拟环境使用virtualenvwrapper的搭建步骤教程图解
2018/09/19 Python
Python3+Pycharm+PyQt5环境搭建步骤图文详解
2019/05/29 Python
python3应用windows api对后台程序窗口及桌面截图并保存的方法
2019/08/27 Python
使用Python的Turtle库绘制森林的实例
2019/12/18 Python
Python猜数字算法题详解
2020/03/01 Python
python 解决Fatal error in launcher:错误问题
2020/05/21 Python
Python常用库Numpy进行矩阵运算详解
2020/07/21 Python
Python脚本实现Zabbix多行日志监控过程解析
2020/08/26 Python
Python调用SMTP服务自动发送Email的实现步骤
2021/02/07 Python
The Hut美国/加拿大:英国领先的豪华在线百货商店
2019/03/26 全球购物
工商治理实习生的自我评价
2014/01/15 职场文书
股东合作协议书
2014/04/14 职场文书
国际商务英语专业求职信
2014/07/08 职场文书
2015年乡镇流动人口工作总结
2015/05/12 职场文书
法律意见书范文
2015/06/04 职场文书
班主任经验交流心得体会
2015/11/02 职场文书
2016优秀护士求职自荐信
2016/01/28 职场文书
css实现左上角飘带效果的完整代码
2022/03/18 HTML / CSS
海弦WR-800F
2022/04/05 无线电
python中validators库的使用方法详解
2022/09/23 Python