最近學習python的高階教程的時候,發現python中__init__ 和 __new__ 比較難以理解,所以在網上又找了很多資料,因爲都比較分散,所以將他們整合到這裏,並添加一些自己的理解和代碼運行試驗。
參考文章來源
python 類中__new__ 和 __init__方法區別
Python面試之理解__new__和__init__的區別
分析
先說一些結論然後分析這兩個函數在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__函數修改了。