python__new__方法以及與裝飾器單例模式比較

在學習面向對象的時候,我們接觸過init方法,它是來初始化類的屬性的,那麼new方法是幹什麼的呢?
1.new方法的定義
new方法是來實例化類的對象的,它返回類的對象(對的常見的obj = cls()就是調用new來返回實例)所以它在init方法前調用,因爲在調用init方法時,類的實例化對象已經生成了
通過下面的例子來理解:

class Foo():

    def __init__(self):
        print("This is init func")

    def __new__(cls, *args, **kwargs):
        print("This is new func")
        # return super(Foo, cls).__new__(cls) #返回類的實例化對象
        # return object.__new__(cls) #同上


obj1 = Foo()

執行結果爲
This is new func
但是,init方法並沒有執行啊?因爲我們只是調用了new方法,但是並沒有返回實例,這個例子只是體現出new方法在init之前,我們取消new方法的註釋,隨便用哪個都行,這兩個return都是返回類的實例

class Foo():

    def __init__(self):
        print("This is init func")

    def __new__(cls, *args, **kwargs):
        print("This is new func")
        return super(Foo, cls).__new__(cls) #返回類的實例化對象
        # return object.__new__(cls)


obj1 = Foo()

執行結果爲:
This is new func
This is init func
可以看到,先用new返回實例化對象,再執行的init方法

2.使用new方法返回單例
單例在程序設計中是比較常用的設計模式,它用於類只能實例化一個對象,比如某個程序只能執行出現一個窗口,不能同時運行多個,這個大多就是通過單例模式來實現的

class Foo():

    def __init__(self, name):
        self.name = name

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, 'instance'):  # 當對象不存在instance屬性,即類還沒有被實例化時,返回實例化對象
            cls.instance = super(Foo, cls).__new__(cls)  # 創建當前類的對象,相當於object.__new__(cls)
        return cls.instance


obj1 = Foo("123")
print(obj1.name)
obj2 = Foo("321")
print(obj1 == obj2, obj1.name, obj2.name, id(obj1), id(obj2))

執行結果爲:
123
True 321 321 2239300270792 2239300270792

這裏的核心思想是,定義instance屬性(隨便什麼名都行,代表這個屬性是類的實例化對象),判斷這個屬性是否存在,若不存在,則創建實例對象,若存在則直接返回實例
而且我們可以看到,當我們重新實例化這個對象時,其實這個對象的id沒有變化,但是屬性改變了,記住這點,我們接下來會與裝飾器單例進行比較

3.裝飾器單例模式

既然都說到單例模式了,不得不提的是裝飾器的單例模式,那麼裝飾器單例的實現方式如下列代碼:

def decorator_single_obj(cls, *args, **kwargs):
    instance = {}  # 創建字典來保存實例

    def get_instance(*args, **kwargs):
        if cls not in instance:  # 若實例不存在則新建
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]

    return get_instance


@decorator_single_obj
class Foo(object):
    def __init__(self, name):
        self.name = name


obj1 = Foo("123")
print(obj1.name, id(obj1))
obj2 = Foo("321")
print(obj2.name, id(obj2))

我們可以看到,雖然也還是實現了單例模式,但是第二次實例化得時候,屬性並沒有被改變
所以裝飾器單例與new單例的區別在於屬性不會被覆蓋
因爲new會重新調用init方法,爲實例重新初始化屬性,而裝飾器單例模式則是直接返回之前生成的對象,並不會重新初始化對象,所以new單例從某種程度上也可以視作僞單例模式

最後呢,再留個彩蛋
對於上面的代碼我們加上一行
obj3=object.n__new__(Foo)
會發生什麼?

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