python的new與init

基於文章:Why is init() always called after new()?code

特別說明:對象

  1. 這篇文章的靈感來源於stackoverflow的一個提問,準確說,我只是作了些知識梳理的工做,對其中的優秀回答進行了總結和提煉,旨在幫助本身和讀者對此有個深入的認識。內存

  2. 本文章節中的new是__new__的簡寫,init是__init__的簡稱,只是爲了語言敘述的方便而作出的省略。get

Part I: 爲何new老是先於init?

new是靜態的類方法,static method。
init是實例方法it

它們有不一樣的參數與返回值:io

  1. new的參數老是:cls 返回值老是self
  2. init的參數老是:self 老是無返回值

Part II: 三段代碼的思考

Block 1: 基於原文的代碼

class B(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in B._dict:
            print("EXISTS:", B._dict['key'])
            return B._dict['key']
        else:
            print("NEW")
            return super(B, cls).__new__(cls)

    def __init__(self):
        print("INIT")
        B._dict['key'] = self
        print("")


b1 = B()
b2 = B()
b3 = B()

# 運行結果以下:
NEW
INIT

EXISTS: <__main__.B object at 0x0000028F29820828>
INIT

EXISTS: <__main__.B object at 0x0000028F29820828>
INIT

關於原文中的代碼解釋:class

在每次實例化以前,也就是new的操做時,都查詢了是否存在第一個實例。在第一次初始化時,把第一個實例self賦值給了類的公有屬性:test_dict。以後的每次實例化以前,new都會查詢到它(實例)已經存在,new而且老是返回了第一個實例給了init。而後init參與對該實例(老是第一個實例)進行構建工做。test

我只能揣測,原問題的做者彷佛意外建立了一個單例的類。你能夠在init中執行print(self),會發現打印出來的對象老是第一個實例。固然,若是確實是想要實現單例模式,應該使用裝飾器。即:object

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

爲何instances使用的是字典?由於Python老是用字典去存儲一個新的實例,而且init中的屬性也一樣以字典的方法存儲的。固然,若是你想節約內存,不使用字典而用列表,能夠重載new方法,或者使用__slot__方法

Block 2: 基於Block 1 的修改

class A(object):
    test_dict = dict()                   

    def __new__(cls):                    
        if 'key' in A.test_dict:
            print("EXISTS:", A.test_dict['key'])
            return A.test_dict['key']
        else:
            print("NEW")
            return super(A, cls).__new__(cls)

    def __init__(self):                  
        print("INIT")
        if 'key' in A.test_dict:
            print('修改')
            A.test_dict['key'] += 1
        else:
            print('第一次建立')
            A.test_dict['key'] = 0
        print('')


a1 = A()
a2 = A()
a3 = A()

Block 3: 基於Block 2 的修改

class A(object):
    test_dict = dict()                    1.類的公有屬性

    def __new__(cls):                     2.執行__new__
        if 'key' in A.test_dict:
            print("EXISTS:", A.test_dict['key'])
            return super(A, cls).__new__(cls)
        else:
            print("NEW")
            return super(A, cls).__new__(cls)

    def __init__(self):                  
        print("INIT")
        if 'key' in A.test_dict:
            print('修改')
            A.test_dict['key'] += 1
        else:
            print('第一次建立')
            A.test_dict['key'] = 0
        print('')


a1 = A()
a2 = A()
a3 = A()

Q&A

  1. new 老是先於 int 執行,爲何?
    答:
    注意事項: new是類的靜態方法,
    Use new when you need to control the creation of a new instance.
    Use init when you need to control initialization of a new instance.
    前者用於控制建立一個新實例,後者用於控制對該新實例進行初始化。

精要:
它們負責的工做不一樣:前者負責建立新實例,後者負責實例初始化
它們服務的層級不一樣:前者是cls(類),後者是self(實例自己)
__new__是祕書給領導打工,後者領導的一衆小弟們打工。

  1. 爲何上述代碼的__init__沒有被執行,而下面的每次都正常執行了?
    答:
    用於我在上述代碼中惡意使用了new, 沒有對new進行返回實例。
    __new__老是第一個被Call(調用),而且他的返回值:當且僅當是一個新的實例!!
    代碼1中,並無返回這個新的實例,第一次實例化對象,自動進行了int,後面的, 都是隻建立了新示例
相關文章
相關標籤/搜索