詳解Python中的__init__和__new__(靜態方法)

1、init 方法是什麼?python

使用Python寫過面向對象的代碼的同窗,可能對 init 方法已經很是熟悉了,init 方法一般用在初始化一個類實例的時候。例如:設計模式

#-*- coding: utf-8 -*-
 
class Person(object):
    """Silly Person"""
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)
 
if __name__ == '__main__':
    piglei = Person('piglei', 24)
    print piglei
複製代碼

這樣即是__init__最普通的用法了。但__init__其實不是實例化一個類的時候第一個被調用 的方法。 當使用 Persion(name, age) 這樣的表達式來實例化一個類時,最早被調用的方法 實際上是 new 方法。學習

1、init 方法是什麼?spa

使用Python寫過面向對象的代碼的同窗,可能對 init 方法已經很是熟悉了,init 方法一般用在初始化一個類實例的時候。例如:設計

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
 #-*- coding: utf-8 -*-
 
class Person(object):
    """Silly Person"""
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)
 
if __name__ == '__main__':
    piglei = Person('piglei', 24)
    print piglei
複製代碼

這樣即是__init__最普通的用法了。但__init__其實不是實例化一個類的時候第一個被調用 的方法。 當使用 Persion(name, age) 這樣的表達式來實例化一個類時,最早被調用的方法 實際上是 new 方法。code

2、new 方法是什麼? __new__方法接受的參數雖然也是和__init__同樣,但__init__是在類實例建立以後調用,而 __new__方法正是建立這個類實例的方法。視頻

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
# -*- coding: utf-8 -*-
 
class Person(object):
    """Silly Person"""
 
    def __new__(cls, name, age):
        print '__new__ called.'
        return super(Person, cls).__new__(cls, name, age)
 
    def __init__(self, name, age):
        print '__init__ called.'
        self.name = name
        self.age = age
 
    def __str__(self):
        return '<Person: %s(%s)>' % (self.name, self.age)
 
if __name__ == '__main__':
    piglei = Person('piglei', 24)
    print piglei
複製代碼

執行結果:對象

piglei@macbook-pro:blog$ python new_and_init.py
__new__ called.
__init__ called.
<Person: piglei(24)>
複製代碼

經過運行這段代碼,咱們能夠看到,__new__方法的調用是發生在__init__以前的。其實當 你實例化一個類的時候,具體的執行邏輯是這樣的:blog

  • p = Person(name, age)
  • 首先執行使用name和age參數來執行Person類的__new__方法,這個__new__方法會 返回Person類的一個實例(一般狀況下是使用 super(Persion, cls).new(cls, … …) 這樣的方式),
  • 而後利用這個實例來調用類的__init__方法,上一步裏面__new__產生的實例也就是 __init__裏面的的 self 因此,initnew 最主要的區別在於:
    • init 一般用於初始化一個新實例,控制這個初始化的過程,好比添加一些屬性, 作一些額外的操做,發生在類實例被建立完之後。它是實例級別的方法。
    • new 一般用於控制生成一個新實例的過程。它是類級別的方法。 可是說了這麼多,new__最一般的用法是什麼呢,咱們何時須要__new

3、new 的做用繼承

依照Python官方文檔的說法,__new__方法主要是當你繼承一些不可變的class時(好比int, str, tuple), 提供給你一個自定義這些類的實例化過程的途徑。還有就是實現自定義的metaclass。 首先咱們來看一下第一個功能,具體咱們能夠用int來做爲一個例子:

假如咱們須要一個永遠都是正數的整數類型,經過集成int,咱們可能會寫出這樣的代碼。

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
class PositiveInteger(int):
    def __init__(self, value):
        super(PositiveInteger, self).__init__(self, abs(value))
 
i = PositiveInteger(-3)
print i
複製代碼

但運行後會發現,結果根本不是咱們想的那樣,咱們任然獲得了-3。這是由於對於int這種 不可變的對象,咱們只有重載它的__new__方法才能起到自定義的做用。 這是修改後的代碼:

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
class PositiveInteger(int):
    def __new__(cls, value):
        return super(PositiveInteger, cls).__new__(cls, abs(value))
 
i = PositiveInteger(-3)
print i
複製代碼

經過重載__new__方法,咱們實現了須要的功能。 另一個做用,關於自定義metaclass。其實我最先接觸__new__的時候,就是由於須要自定義 metaclass,但鑑於篇幅緣由,咱們下次再來說python中的metaclass和__new__的關係。

4、用__new__來實現單例

事實上,當咱們理解了__new__方法後,咱們還能夠利用它來作一些其餘有趣的事情,好比實現 設計模式中的 單例模式(singleton) 。 由於類每一次實例化後產生的過程都是經過__new__來控制的,因此經過重載__new__方法,咱們 能夠很簡單的實現單例模式。

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
class Singleton(object):
    def __new__(cls):
        # 關鍵在於這,每一次實例化的時候,咱們都只會返回這同一個instance對象
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance
 
obj1 = Singleton()
obj2 = Singleton()
 
obj1.attr1 = 'value1'
print obj1.attr1, obj2.attr1
print obj1 is obj2
複製代碼

輸出結果:

value1 value1
True
複製代碼

能夠看到obj1和obj2是同一個實例。

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
class A(object):
    def __init__(self):
        print "init"
    def __new__(cls,*args, **kwargs):
        print "new %s"%cls
        return object.__new__(cls, *args, **kwargs)
 
A()
複製代碼

輸出:

new <class '__main__.A'>
init
複製代碼

知識點:

繼承自object的新式類纔有__new__

  • __new__至少要有一個參數cls,表明要實例化的類,此參數在實例化時由Python解釋器自動提供

  • __new__必需要有返回值,返回實例化出來的實例,這點在本身實現__new__時要特別注意,能夠return父類__new__出來的實例,或者直接是object的__new__出來的實例

  • __init__有一個參數self,就是這個__new__返回的實例,__init__在__new__的基礎上能夠完成一些其它初始化的動做,__init__不須要返回值

若__new__沒有正確返回當前類cls的實例,那__init__是不會被調用的,即便是父類的實例也不行

''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:857662006 尋找有志同道合的小夥伴,互幫互助, 羣裏還有不錯的視頻學習教程和PDF電子書! '''
class A(object):
    pass
 
class B(A):
    def __init__(self):
        print "init"
    def __new__(cls,*args, **kwargs):
        print "new %s"%cls
        return object.__new__(A, *args, **kwargs)
 
b=B()
print type(b)
複製代碼

輸出:

new <class '__main__.B'>
<class '__main__.A'>
複製代碼
相關文章
相關標籤/搜索