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

__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是同一个实例。

(0)

相关推荐

  • 详解Python中的__init__和__new__

    一.__init__ 方法是什么?使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: 复制代码 代码如下: # -*- coding: utf-8 -*- class Person(object):    """Silly Person""" def __init__(self, name, age):        self.name = name

  • Python中的__new__与__init__魔术方法理解笔记

    很喜欢Python这门语言.在看过语法后学习了Django 这个 Web 开发框架.算是对 Python 有些熟悉了.不过对里面很多东西还是不知道,因为用的少.今天学习了两个魔术方法:__new__ 和 __init__. 开攻: 如果对 Python 有所简单了解的话应该知道它包含类这个概念的.语法如下: 复制代码 代码如下: class ClassName:     <statement - 1>:         .         .           .     <state

  • Python中__new__与__init__方法的区别详解

    在python2.x中,从object继承得来的类称为新式类(如class A(object))不从object继承得来的类称为经典类(如class A()) 新式类跟经典类的差别主要是以下几点: 1. 新式类对象可以直接通过__class__属性获取自身类型:type 2. 继承搜索的顺序发生了改变,经典类多继承时属性搜索顺序: 先深入继承树左侧,再返回,开始找右侧(即深度优先搜索);新式类多继承属性搜索顺序: 先水平搜索,然后再向上移动 例子: 经典类: 搜索顺序是(D,B,A,C) >>

  • 详解Python中的__new__()方法的使用

    先看下object类中对__new__()方法的定义: class object: @staticmethod # known case of __new__ def __new__(cls, *more): # known special case of object.__new__ """ T.__new__(S, ...) -> a new object with type S, a subtype of T """ pass obj

  • Python中__init__和__new__的区别详解

    __init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: # -*- coding: utf-8 -*- class Person(object): """Silly Person""" def __init__(self, name, age): self.name = name self.age = age def __

  • 详解Python中的__new__、__init__、__call__三个特殊方法

    __new__: 对象的创建,是一个静态方法,第一个参数是cls.(想想也是,不可能是self,对象还没创建,哪来的self) __init__ : 对象的初始化, 是一个实例方法,第一个参数是self. __call__ : 对象可call,注意不是类,是对象. 先有创建,才有初始化.即先__new__,而后__init__. 上面说的不好理解,看例子. 1.对于__new__ class Bar(object): pass class Foo(object): def __new__(cls

  • 在Python的Django框架中实现Hacker News的一些功能

    逐步指示 这是提供给更喜欢阅读的人的视频文本版本.我们将创建一个类似黑客新闻 或Reddit的社交新闻网站.它将被称为"钢铁传闻",作为一个分享关于"钢铁侠"的有趣传闻和对其进行投票的地方. 屏幕录像第一部分的概述: 目标 虚拟环境- 从零开始! 模型管理 - 梦想的工作 #78 基本的模板 通用视图 - 新闻列表视图和 新闻细节视图 分页-免费!! 设置虚拟环境 我们将用virtualenv和 virtualenvwrapper来建立一个虚拟开发环境.首先请确认你

  • python中的__init__ 、__new__、__call__小结

    1.__new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身2.__init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化:注意,这里的第一个参数是self即对象本身[注意和new的区别]3.__call__(self,  *args, **kwargs) 如果类实现了这个方法,相当于把这个类型

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

    __new__ 方法是什么? 如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参 数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货.而 __new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品. __new__()方法的特性: 1.__new__()方

  • 通过实例解析python and和or使用方法

    在python中and和or返回的值并不是True和false这么简单.虽然他们看上去和c++中的&&和||有些相似.在了解and和or之前,我们先要了解python中的True和False. 在python里面,0.''.[].().{}.None为假,其它任何东西都为真.ok,在此前提下.看这样一个例子: 我们大致可以总结出这样的一个规律,对于and,如果没有假值,返回的是最后一个真值,如果有假值,则返回的是第一个假值. 对于or,如果没有真值,返回的是最后一个假值,如果有真值,则返回

  • 解析Python中的__getitem__专有方法

    __getitem__ 来看个简单的例子就明白: def __getitem__(self, key): return self.data[key] >>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") >>> f {'name':'/music/_singles/kairo.mp3'} >>> f.__getitem__("name") '/music/_

  • 通过实例解析Python RPC实现原理及方法

    单线程同步 使用socket传输数据 使用json序列化消息体 struct将消息编码为二进制字节串,进行网络传输 消息协议 // 输入 { in: "ping", params: "ireader 0" } // 输出 { out: "pong", result: "ireader 0" } 客户端 client.py # coding: utf-8 # client.py import json import time i

  • Python中class内置方法__init__与__new__作用与区别解析

    目录 背景 __init__方法作用 __new__方法作用 __init__ && __new__联系 使用__new__的场景 定义.继承immutable class 使用metaclass 参考文献 背景 最近尝试了解Django中ORM实现的原理,发现其用到了metaclass(元类)这一技术,进一步又涉及到Python class中有两个特殊内置方法__init__与__new__,决定先尝试探究一番两者的具体作用与区别.PS: 本文中涉及的类均为Python3中默认的新式类,

  • Python中使用__new__实现单例模式并解析

    单例模式是一个经典设计模式,简要的说,一个类的单例模式就是它只能被实例化一次,实例变量在第一次实例化时就已经固定. 在Python中常见的单例模式有None,这就是一个很典型的设计,通常使用 if xxx is None或者if xxx is not None来比较运算. Python实现单例模式 代码如下: class MyClass: _instance = None _first_init = False def __new__(cls, *args, **kwargs): if not

  • Python解析xml中dom元素的方法

    本文实例讲述了Python解析xml中dom元素的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: from xml.dom import minidom try:     xmlfile = open("path.xml", "a+")     #xmldoc = minidom.parse( sys.argv[1])     xmldoc = minidom.parse(xmlfile) except :     #updatelogger.

  • python中列表元素连接方法join用法实例

    本文实例讲述了python中列表元素连接方法join用法.分享给大家供大家参考.具体分析如下: 创建列表: >>> music = ["Abba","Rolling Stones","Black Sabbath","Metallica"] >>> print music 输出: ['Abba', 'Rolling Stones', 'Black Sabbath', 'Metallica']

  • python中base64加密解密方法实例分析

    本文实例讲述了python中base64加密解密方法.分享给大家供大家参考.具体分析如下: 一.base64 Base64是一种基于64个可打印字符来表示二进制数据的表示方法.由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符.三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示.它可用来作为电子邮件的传输编码.在Base64中的可打印字符包括字母A-Z.a-z.数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中而不同.编码后的

  • Python中的对象,方法,类,实例,函数用法分析

    本文实例分析了Python中的对象,方法,类,实例,函数用法.分享给大家供大家参考.具体分析如下: Python是一个完全面向对象的语言.不仅实例是对象,类,函数,方法也都是对象. 复制代码 代码如下: class Foo(object):     static_attr = True     def method(self):         pass foo = Foo() 这段代码实际上创造了两个对象,Foo和foo.而Foo同时又是一个类,foo是这个类的实例. 在C++里类型定义是在编

随机推荐