python類中 __new__ 和__init__的區別和聯繫

        最近學習python的高階教程的時候,發現python中__init__ 和 __new__ 比較難以理解,所以在網上又找了很多資料,因爲都比較分散,所以將他們整合到這裏,並添加一些自己的理解和代碼運行試驗。

 

參考文章來源

python 類中__new__ 和 __init__方法區別

Python面試之理解__new__和__init__的區別

陌生的 metaclass

分析

先說一些結論然後分析這兩個函數在python的代碼中是如何進行解釋的

1)、__new__是一個靜態方法,而__init__是一個實例方法

2)、__new__方法會返回一個創建的實例,而__init__什麼都不返回

3)、只有在__new__返回一個cls的實例時,後面的__init__才能被調用

4)、當創建一個新實例時調用__new__,初始化一個實例時用__init__

5)、__init__ 方法爲初始化方法, __new__方法纔是真正的構造函數。

        從上面羅列出來的結論上來看,__new__和__init__的區別在於,__new__是提供實例,而__init__則提供實例的初始化,因此如何理解實例與初始化的關係就是其區別的關鍵,我們來看一段代碼。平常我們在學習的過程中會接觸到的函數一般是__init__函數,而很少會寫__new__,一個普通的Man類的實現如下

class Man:
    def __init__(self):
        print('__init__.A man.')
a=Man()
代碼輸出結果爲:
__init__.A man.

       調用Man類後,類會自動執行__init__函數,實現實例的初始化,看起來和__new__沒有關係,但是實際上python 3.x中任何一個類都會繼承一個object的基類,並繼承基類的__new__方法。因此我們重構上面的Man類的__new__函數。

class Man(object):
    def __new__(cls, *args, **kwargs):
        instance=object.__new__(cls,*args, **kwargs)
        print('__new__.A man.')
        return instance

    def __init__(self):
        print('__init__.A man.')

a=Man()
代碼輸出
__new__.A man.
__init__.A man.

可以看到在調用__init__進行函數的初始化之前,類先調用了__new__方法,並且返回了一個instance對象,之後調用了函數的__init__方法。注意對於__new__函數而言,其必須有放回值,且這個返回值爲類的實例,否則無法進行實例的初始化。而__init__則不允許有返回值。

TypeError: __init__() should return None, not 'int'

__new__的一些重要的功能

singleTon模式

這個模式指的的單例模式,及對於某一個類而言,這個類只會創建唯一的一個實例,其實現如下代碼:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)

        return cls._instance

s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2) 

代碼輸出爲:
<__main__.Singleton object at 0x000002201763BF28>
<__main__.Singleton object at 0x000002201763BF28>

如上的代碼我們可以看到,雖然代碼初始化了類的兩個實例,但是由輸出信息可以看到,兩個實例存儲在相同的位置,實際上爲統一實例。容易看出__new__是爲類提供實例的函數。

工廠模式

工廠模式指的是類可以根據輸入信息實現自定義類轉換的方式,對於python而言可以這樣操作

class Fruit(object):
    def __init__(self,k):
        pass

    def print_color(self):
        pass

class Apple(Fruit):
    def __init__(self):
        print('this is an apple')
        pass

    def print_color(self):
        print("apple is in red")

class Orange(Fruit):
    def __init__(self):
        print('this is an orange')
        pass


    def print_color(self):
        print("orange is in orange")

class FruitFactory(object):
    fruits = {"apple": Apple, "orange": Orange}

    def __new__(cls, name):
        if name in cls.fruits.keys():
            return cls.fruits[name]()
        else:
            return Fruit()


    def __init__(self):
        print('this is fruitfactory')

fruit1 = FruitFactory("apple")
fruit2 = FruitFactory("orange")
fruit1.print_color()
fruit2.print_color()
  

代碼輸出:
this is an apple
this is an orange
apple is in red
orange is in orange

工廠模式中__new__接收參數,並返回相應的類的實例,並執行相應的初始化,而不執行自身的初始化函數。

metaclass元類

metaclass可以通過__new__函數實現對類的定製

#這個類的作用是將類的所有屬性和方法前面都加上一個my_

class PrefixMetaclass(type):
    def __new__(cls, name, bases, attrs):
        # 給所有屬性和方法前面加上前綴 my_
        _attrs = (('my_' + name, value) for name, value in attrs.items())  

        _attrs = dict((name, value) for name, value in _attrs)  # 轉化爲字典
        _attrs['echo'] = lambda self, phrase: phrase  # 增加了一個 echo 方法

        return type.__new__(cls, name, bases, _attrs)  # 返回創建後的類



class Foo(metaclass=PrefixMetaclass):
    name = 'foo'
    def bar(self):
        print 'bar'



測試輸出
f = Foo()
>>> f.name    # name 屬性已經被改變
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-774-4511c8475833> in <module>()
----> 1 f.name

AttributeError: 'Foo' object has no attribute 'name'
>>>
>>> f.my_name
'foo'
>>> f.my_bar()
bar
>>> f.echo('hello')
'hello'

可以看到所有的方法以及屬性都被元類的__new__函數修改了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章