Python __new__ 和 __init__ 的区别

__new__() 是在新式类中新出现的方法,它作用在构造方法( __init__() )建造实例之前. 可以这么理解,在 Python 中存在于类里面的构造方法 __init__() 负责将类进行实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为 __new__() 也可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。

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

__new__() 方法的特性:

  • __new__() 方法是在类准备将自身实例化时调用。
  • __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。

类的实例化和构造方法通常是这个样子:

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

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

__new__ 的参数解释:

  • cls 表示当前类.
  • *args**kwargs 分别表示该类进行初始化时, 输入的位置参数和命名参数。
    __new__ 函数一般会有返回语句, 在返回语句中: object.__new__() 表示调用 object 类的 __new__() 函数, 也可以使用 super().__new__(), 表示调用当前类的父类的 __new__(), 如果父类没有自定义 __new__(), 就会调用该父类的父类的 __new__(), 以此类推, 直到 object 类. return 中的 __new__() 函数的第一个参数 cls 表示将要返回的类的类型, cls 表示用于返回当前类, 也可以返回其他类.

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

如果新式类中重写了 __new__() 方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,只有新式类必定都有 __new__(),因为所有新式类都是 object 的后代,而经典类则没有 __new__() 方法)的 __new__() 方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。具体看以下代码解释:

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)    

class Child(Foo):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)
        
class Stranger(object):
    def __new__(cls, *args, **kwargs):
        ...
  • 如果Child中没有定义 __new__() 方法,那么会自动调用其父类的 __new__() 方法来制造实例,即 Foo.__new__(cls, *args, **kwargs)
  • 在任何新式类的 __new__() 方法,不能调用自身的 __new__() 来制造实例,因为这会造成死循环。因此必须避免类似以下的写法:
  1. Foo 中避免:return Foo.__new__(cls, *args, **kwargs)return cls.__new__(cls, *args, **kwargs)Child 同理。
  2. 使用 object 或者没有血缘关系的新式类的 __new__() 是安全的,但是如果是在有继承关系的两个类之间,应避免互调造成死循环,例如: (Foo) return Child.__new__(cls) , (Child) return Foo.__new__(cls)
  • 在制造 Stranger 实例时,会自动调用 object.__new__(cls).

通常来说,新式类开始实例化时,__new__() 方法会返回 clscls 指代当前类)的实例,然后该类的 __init__() 方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入 __new__() 方法中接收的位置参数和命名参数。

__new__() 除了返回 cls (当前类) 的实例之外,还可以有其他用法:

  • 返回其他类:
class Foo(object):
    def __new__(cls, *args, **kwargs):
        return object.__new__(Stranger, *args, **kwargs) 
        
    def __init__(self, *args, **kwargs):
        ... 

class Stranger(object):
    ...

foo = Foo()
print(type(foo))    

打印的结果显示 foo 其实是 Stranger 类的实例。

  • 返回其他数据类型:
a = 10

class Foo(object):
    def __new__(cls, *args, **kwargs):
        print("__new__ is called.")
        return a
    def __init__(self, *args, **kwargs):
        ...
Foo()

执行结果为:

__new__ is called
10

可以这么描述 __new__()__ini__() 的区别,在新式类中 __new__() 才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法 __init__() 使其丰满。

如果以建房子做比喻,__new__() 方法负责开发地皮,打下地基,并将原料存放在工地。而 __init__() 方法负责从工地取材料建造出地皮开发招标书中规定的大楼,__init__() 负责大楼的细节设计,建造,装修使其可交付给客户。

注意:如果 __new__() 没有返回 cls(即当前类)的实例,那么当前类的 __init__() 方法是不会被调用的。如果 __new__() 返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,715评论 0 9
  • 文/乐僧 很多人想跟我一样学习写作。但不知如何开始,这就需要老师指导,至少需要看一些这方面的指导书。 上网搜索知乎...
    乐僧书田阅读 363评论 0 7
  • 姓名:柴吉毅 慈溪市创强电气有限公司 【日精进打卡第13天】 【知~学习】 看了《活法》一篇章 《六项精进大纲》1...
    大大大大宝_1ed2阅读 207评论 0 0
  • 运行环境 python3.6.7、tensorflow1.4.0 思路 这里用numpy对cos函数进行间隔采样,...
    YinliX阅读 1,348评论 0 0