实例解析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实现socket端口重定向示例
Feb 10 Python
python判断windows隐藏文件的方法
Mar 21 Python
python操作xml文件详细介绍
Jun 09 Python
python字典排序实例详解
May 20 Python
pymssql数据库操作MSSQL2005实例分析
May 25 Python
selenium+python自动化测试之页面元素定位
Jan 23 Python
解决python3 安装不了PIL的问题
Aug 16 Python
Python3 JSON编码解码方法详解
Sep 06 Python
win7下 python3.6 安装opencv 和 opencv-contrib-python解决 cv2.xfeatures2d.SIFT_create() 的问题
Oct 24 Python
基于TensorFlow中自定义梯度的2种方式
Feb 04 Python
Python内建序列通用操作6种实现方法
Mar 26 Python
基于Python的图像阈值化分割(迭代法)
Nov 20 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正则表达式使用的详细介绍
2013/04/27 PHP
curl不使用文件存取cookie php使用curl获取cookie示例
2014/01/26 PHP
经验几则 推荐
2006/09/05 Javascript
告诉大家什么是JSON
2008/06/10 Javascript
JavaScript监测ActiveX控件是否已经安装过的代码
2008/09/02 Javascript
引用外部js乱码问题分析及解决方案
2013/04/12 Javascript
jQuery下实现等待指定元素加载完毕(可改成纯js版)
2013/07/11 Javascript
js使用for循环查询数组中是否存在某个值
2014/08/12 Javascript
angularjs指令中的compile与link函数详解
2014/12/06 Javascript
JQuery中的事件及动画用法实例
2015/01/26 Javascript
使用coffeescript编写node.js项目的方法汇总
2015/08/05 Javascript
js时间查询插件使用详解
2017/04/07 Javascript
原生js实现密码输入框值的显示隐藏
2017/07/17 Javascript
详解vue2.6插槽更新v-slot用法总结
2019/03/09 Javascript
微信小程序地图绘制线段并且测量(实例代码)
2020/01/02 Javascript
python连接mysql并提交mysql事务示例
2014/03/05 Python
python操作ssh实现服务器日志下载的方法
2015/06/03 Python
windows下Python实现将pdf文件转化为png格式图片的方法
2017/07/21 Python
Python中用字符串调用函数或方法示例代码
2017/08/04 Python
浅谈python可视化包Bokeh
2018/02/07 Python
Python使用win32 COM实现Excel的写入与保存功能示例
2018/05/03 Python
PyTorch中 tensor.detach() 和 tensor.data 的区别详解
2020/01/06 Python
Python3变量与基本数据类型用法实例分析
2020/02/14 Python
Python字符串格式化f-string多种功能实现
2020/05/07 Python
Pytest单元测试框架如何实现参数化
2020/09/05 Python
深入理解css属性的选择对动画性能的影响
2016/04/20 HTML / CSS
HTML5中使用postMessage实现Ajax跨域请求的方法
2016/04/19 HTML / CSS
HTML5在IE10、火狐下中文乱码问题的解决方法
2013/11/18 HTML / CSS
中国海淘族值得信赖的海淘返利网站:55海淘
2017/01/16 全球购物
乐天旅游台湾网站:Rakuten Travel TW
2017/06/01 全球购物
联想瑞士官方网站:Lenovo Switzerland
2017/11/19 全球购物
用Python匹配HTML tag的时候,<.*>和<.*?>有什么区别
2012/11/04 面试题
GC是什么?为什么要有GC?
2013/12/08 面试题
八一建军节营销活动方案
2014/08/31 职场文书
小班上学期幼儿评语
2014/12/30 职场文书
2016年党建工作简报
2015/11/26 职场文书