一問搞懂python的__init__和__new__方法
一、文字概述
__new__是在例項建立之前被呼叫的,因為它的任務就是建立例項然後返回該例項,是個靜態方法。
__init__是當例項物件建立完成後被呼叫的,然後設定物件屬性的一些初始值。
故而“ 本質上 ”來說,__new__()方法負責建立例項,而__init__()僅僅是負責例項屬性相關的初始化而已,執行順序是,先new後init。
二、new和init的執行順序
1、當定義類的時候,不定義__new__()方法,這也是我們平時定義類的時候常見的方式。程式碼如下:
class Student(object):
def __init__(self,name,age):
self.name=name
self.age=age
print('我是init')
def study(self):
print('我愛學習!')
if __name__=='__main__':
s=Student('張三',25)
print(s.name)
print(s.age)
s.study()
執行結果為:
我是init
張三
25
我愛學習!
-------------------------------------------------------------------------------------------------------------------------------------------------------------
2、當定義類的時候自己定義了__new__()方法,
程式碼如下:
class Student(object):
def __new__(cls,*args,**kwargs):
print('我是new函式!') #這是為了追蹤new的執行過程
print(type(cls)) #這是為了追蹤new的執行過程
return object.__new__(cls) #呼叫父類的(object)的new方法,返回一個Student例項,這個例項傳遞給init的self引數
def __init__(self,name,age):
self.name=name
self.age=age
print('我是init')
def study(self):
print('我愛學習!')
if __name__=='__main__':
s=Student('張三',25)
print(s.name)
print(s.age)
s.study()
執行結果為:
我是new函式!
<class 'type'>
我是init
張三
25
我愛學習!
由此可見的確是先執行new,再執行init。
3、從自定義的類繼承
前面的Student類是從object繼承的,現在從自定義繼承,程式碼如下:
class Parent:
def __new__(cls,*args,**kwargs):
print('我是父親類的new')
print(type(cls))
return object.__new__(cls)
def __init__(self):
print('我是父親類的init')
class Children(Parent):
def __init__(self,name,age):
self.name=name
self.age=age
print('我是子類的init')
if __name__=='__main__':
child=Children('李四',30)
print(child.name)
print(child.age)
執行結果為:
我是父親類的new
<class 'type'>
我是子類的init
李四
30
從上面可以看出,如果某個類自己未定義new,則執行的是父類的new函式。
三、__new__()一定要有返回值
前面說到,new函式實際上就是建立當前類的例項的,該例項然後傳遞給init的self引數,進行例項的初始化,故而如果new函式不返回值,則無法構建例項,如下:
class Student(object):
def __new__(cls,*args,**kwargs):
print('我是new函式!') #這是為了追蹤new的執行過程
print(type(cls)) #這是為了追蹤new的執行過程
def __init__(self,name,age):
self.name=name
self.age=age
print('我是init')
def study(self):
print('我愛學習!')
if __name__=='__main__':
s=Student('張三',25)
print(s.name)
print(s.age)
s.study()
執行結果為:
我是new函式!
<class 'type'>
AttributeError: 'NoneType' object has no attribute 'name'
從上面可見,雖然在建立s例項的過程中,呼叫了new函式,列印出了相關的值,但是因為沒有返回值,故而沒能夠建立例項s,那麼在初始化init的時候自然會出錯,這就是為什麼會顯示沒有name屬性的原因了,因為根本沒建立成功!
四、new函式到底定義在哪裡?
實際上,new函式並不是定義在object中,它定義在 元類 type裡面,可以通過檢視 help(type) 或者是help(type.__new__)
進行檢視,__new__是type的成員,而python中所有的類都是type的例項,包括object,故而通過object,Student.__new__()的形式初始化就在正常不過了,這不就是“ 例項.方法 ”嗎?
new的定義如下:
__new__(*args, **kwargs) method of builtins.type instance
Create and return a new object. See help(type) for accurate signature.
這就是為什麼很多人在自定義new的時候寫成,如下形式,也就不足為怪了:
def __new__(cls,*args,**kwargs):
print('我是new函式!') #這是為了追蹤new的執行過程
print(type(cls)) #這是為了追蹤new的執行過程
return object.__new__(cls) #呼叫父類的(object)的new方法,返回一個Student例項,這個例項傳遞給init的self引數
因為這不就是和定義一樣嗎?雖然多了一個cls,實際上因為是*args的關係,這並不會有影響,這個引數實際上就是要建立的那個類的例項所屬的型別,如Student。
五、new的主要應用
1、__new__方法主要是當你繼承一些不可變的class時(比如int, str, tuple), 提供給你一個自定義這些類的例項化過程的途徑
2、還有就是實現自定義的metaclass
3、用__new__來實現單例
詳細使用會在後面詳細說明!
5.1 __new__函式的本質
注意事項:
(1)__new__方法是定義在元類type裡面的,作用就是專門建立例項的。
(2)__new__的本質上是一個“類方法”,故而第一個引數為cls,但是因為系統知道它是類方法,所以有不需要顯式新增@classmethod
(3)__new__必須具有返回值,否則無法建立物件,因為__init__函式需要這個返回值
(4)自己在定義__new__的時候,引數要與__init__函式的引數匹配,我可以不用到這些引數,但一定要匹配。或者可以使用*arg和**args的形式。
例子1:
class Parent:
def __new__(cls,*arg): #這裡是正確的,因為*arg可以匹配任意多個未知引數
return object.__new__(cls)
def __init__(self,age,name):
self.age=age
self.name=name
p=Parent(23,'feifei')
print(p.age)
print(p.name)
例子2:
class Parent:
def __new__(cls,age,name): #正確,雖然沒用age和name,但也要匹配
return object.__new__(cls)
def __init__(self,age,name):
self.age=age
self.name=name
p=Parent(23,'feifei')
print(p.age)
print(p.name)
上面的兩個例子產生的結果為
23
feifei
例子3:
class Parent:
def __new__(cls): #這是不行的,因為引數不匹配
return object.__new__(cls)
def __init__(self,age,name):
self.age=age
self.name=name
p=Parent(23,'feifei')
print(p.age)
print(p.name)
下面詳細解釋建立物件p的過程,
第一步:python會呼叫Parent.__new__(Parent,23,‘name') 函式,它會返回一個物件,即object.__new__(Parent) 物件,這裡的age和name都沒有使用。為什麼呢?因為__new__方法是定義在type元類中的,而object本身又是type元類的一個物件,(不明白這裡的可以看我的文章,關於python元類)這不就是一個簡單的 物件.方法(引數) 的簡單呼叫嗎?
第二步:再呼叫__init__函式進行初始化。
六、總結
1、建立例項的時候,先new後init
2、new定義在type元類中,必須具有返回值,
3、new的作用就是建立例項,然後將建立的例項傳遞給init進行初始化
相關文章
- Python __new__ 和 __init__ 的區別Python
- python中__init__ 和__new__的對比Python
- 簡述Python類中的 __init__、__new__、__call__ 方法Python
- Python中__new__和__init__的區別與聯絡Python
- Python面試之理解__new__和__init__的區別Python面試
- python中的__init__ 、__new__、__call__小結Python
- Python語言中__init__與__new__的區別是什麼?Python
- Python中__init__的用法和理解Python
- Python 中__new__方法詳解及使用Python
- python的__init__幾種方法總結【轉載】Python
- Python中的__init__()方法整理中(兩種解釋)Python
- Python中__init__的理解Python
- python中__new__的使用注意Python
- python 詳解類class的繼承、__init__初始化、super方法Python繼承
- 搞懂 Java equals 和 hashCode 方法Java
- Python類中__del__()、__call__()、__repr__()、__new__()、__hash__()方法Python
- 01 #### `__new__` ,構造方法,建立物件構造方法物件
- 00 #### `__init__` ,初始化方法
- 一文搞懂Python Unittest測試方法執行順序Python
- 徹底搞懂 python 中文亂碼問題Python
- (學習筆記)python 對__init__的初步理解筆記Python
- Python 中的 super(類名, self).__init__() 的含義Python
- 類中的__init__()和__call__()函式函式
- Django __init__ 方法用於初始化Django
- 一、訪問物件屬性和方法的操作物件
- Python 中__init__函式以及引數selfPython函式
- 搞懂鉤子方法和模板方法,看完這篇就夠了
- 一文搞懂如何自己寫一個Python庫Python
- 8張圖搞懂Redis和MySQL資料一致性問題RedisMySql
- Python中訪問私有屬性和私有方法Python
- 徹底搞懂Python中的類Python
- 關於python訪問字典的方法Python
- 一文徹底搞懂Python可迭代(Iterable)、迭代器(Iterator)和生成器(Generator)的概念Python
- 一文搞懂Session和Cookie的用法及區別SessionCookie
- 棧和括號匹配,一文搞懂
- 一文搞懂 CountDownLatch 用法和原始碼!CountDownLatch原始碼
- 詳解Python魔法函式,__init__,__str__,__del__Python函式
- 一起搞懂 CSS 水平居中與垂直居中的16個方法CSS