实例解析Python中的__new__特殊方法


Posted in Python onJune 02, 2016

__new__ 方法是什么?
如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参 数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 __new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。
__new__()方法的特性:
1.__new__()方法是在类准备将自身实例化时调用。
2.__new__()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
类的实例化和它的构造方法通常都是这个样子:

class MyClass(object):
  def __init__(self, *args, **kwargs):
    ...
# 实例化
myclass = MyClass(*args, **kwargs)

正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 __init__()方法之前,Python首先调用__new__()方法:

def __new__(cls, *args, **kwargs):
  ...

第一个参数cls是当前正在实例化的类。
   如果要得到当前类的实例,应当在当前类中的__new__()方法语句中调用当前类的父类 的__new__()方法。
例如,如果当前类是直接继承自object,那当前类的__new__()方法返回的对象应该为:

def __new__(cls, *args, **kwargs):
  ...
  return object.__new__(cls)

(注意:
事实上如果(新式)类中没有重写__new__()方法,即在定义新式类时没有重新定义__new__()时 ,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写 __new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。)

__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__之前的。其实当 你实例化一个类的时候,具体的执行逻辑是这样的:

p = Person(name, age)

首先执行使用name和age参数来执行Person类的__new__方法,这个__new__方法会 返回Person类的一个实例(通常情况下是使用 super(Persion, cls).__new__(cls, ... ...) 这样的方式),
然后利用这个实例来调用类的__init__方法,上一步里面__new__产生的实例也就是 __init__里面的的 self
所以,__init__ 和 __new__ 最主要的区别在于:

__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
__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实现的一个自动售饮料程序代码分享
Aug 25 Python
用Python实现一个简单的能够发送带附件的邮件程序的教程
Apr 08 Python
python实现八大排序算法(1)
Sep 14 Python
Python实现将json文件中向量写入Excel的方法
Mar 26 Python
Python3 读、写Excel文件的操作方法
Oct 20 Python
Django restframework 源码分析之认证详解
Feb 22 Python
Python+OpenCV+pyQt5录制双目摄像头视频的实例
Jun 28 Python
代码实例讲解python3的编码问题
Jul 08 Python
Python爬虫爬取Bilibili弹幕过程解析
Oct 10 Python
python循环输出三角形图案的例子
Nov 22 Python
Python virtualenv虚拟环境实现过程解析
Apr 18 Python
python中温度单位转换的实例方法
Dec 27 Python
详解Python中的__new__、__init__、__call__三个特殊方法
Jun 02 #Python
Python实现优先级队列结构的方法详解
Jun 02 #Python
KMP算法精解及其Python版的代码示例
Jun 01 #Python
Python缩进和冒号详解
Jun 01 #Python
Python注释详解
Jun 01 #Python
深入理解python try异常处理机制
Jun 01 #Python
python学习 流程控制语句详解
Jun 01 #Python
You might like
PHP打开和关闭文件操作函数总结
2014/11/18 PHP
微信支付PHP SDK ―― 公众号支付代码详解
2016/09/13 PHP
PHP实现上传多文件示例代码
2017/02/20 PHP
利用PHP判断是否是连乘数字串的方法示例
2017/07/03 PHP
你必须知道的Javascript知识点之&quot;深入理解作用域链&quot;的介绍
2013/04/23 Javascript
javascript中的onkeyup和onkeydown区别介绍
2013/04/28 Javascript
js身份证判断方法支持15位和18位
2014/03/18 Javascript
window.open()实现post传递参数
2015/03/12 Javascript
javascript中call和apply的用法示例分析
2015/04/02 Javascript
javascript检测两个数组是否相似
2015/05/19 Javascript
论JavaScript模块化编程
2016/03/07 Javascript
JavaScript判断数组重复内容的两种方法(推荐)
2016/06/06 Javascript
又一款js时钟!transform实现时钟效果
2016/08/15 Javascript
jQuery中常用动画效果函数(日常整理)
2016/09/17 Javascript
javascript实现根据汉字获取简拼
2016/09/25 Javascript
bootstrap模态框消失问题的解决方法
2016/12/02 Javascript
超简单的Vue.js环境搭建教程
2017/03/17 Javascript
jQuery的时间datetime控件在AngularJs中的使用实例(分享)
2017/08/17 jQuery
详解KOA2如何手写中间件(装饰器模式)
2018/10/11 Javascript
webpack实践之DLLPlugin 和 DLLReferencePlugin的使用教程
2019/06/10 Javascript
Vue 解决路由过渡动画抖动问题(实例详解)
2020/01/05 Javascript
JS函数进阶之prototy用法实例分析
2020/01/15 Javascript
js实现tab栏切换效果
2020/08/02 Javascript
如何使用gpu.js改善JavaScript的性能
2020/12/01 Javascript
[43:53]OG vs EG 2019国际邀请赛淘汰赛 胜者组 BO3 第三场 8.22
2019/09/05 DOTA
[01:16:50]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第一场 3月7日
2021/03/11 DOTA
python使用urllib2提交http post请求的方法
2015/05/26 Python
python连接数据库的方法
2017/10/19 Python
Python if语句知识点用法总结
2018/06/10 Python
python @propert装饰器使用方法原理解析
2019/12/25 Python
使用CSS3制作一个简单的进度条(demo)
2017/05/23 HTML / CSS
Melijoe美国官网:法国奢侈童装购物网站
2017/04/19 全球购物
英国和爱尔兰最大的地毯零售商:Kukoon
2018/12/17 全球购物
法人授权委托书公证范本
2014/09/14 职场文书
2014年班级工作总结范文
2014/12/23 职场文书
综合办公室岗位职责
2015/04/11 职场文书