使用Python描述符實現單例模式

一、什麼是描述符

在Python中實現了__get__/__set__/__delete__魔術方法的類就是描述符,通過描述符我們可以反向控制引用了描述符的類。

如以下的代碼所示,在普通的類中,類B引用了類A,類A是不能控制類B。

class A:
    pass

class B:
    a = A()

B.a

但是,實現了描述符協議的類就不一樣了,如以下的代碼所示,我們可以通過類A來修改類B的屬性。

class A:
    def __get__(self, instance, owner):
        # owner就是類B
        if not owner.test:
            owner.test = 'foo'  # B.test = 'foo'

    def __set__(self, instance, value):
        pass

    def __delete__(self, instance):
        pass


class B:
    a = A()
    test = None


B.a  # 調用描述符會觸發了A.__get__函數的調用,並把類B當做參數傳進去
print(B.test)  # 輸出foo

 

二、實現單例模式

利用描述符反向控制的特點可以實現單例模式,具體代碼如下,我們把單例實例化放在描述符類Descriptor的__get__函數來實現,只有觸發__get__函數既可以,同時重寫Singleton類的__new__函數,裏面拋出異常即可,防止隨意實例化,只能通過get_instance獲得實例(借鑑了C++實現單例的一種方法:把構造函數設置爲私有,通過一個靜態函數返回實例)。

PS:爲了代碼簡潔,先不考慮線程安全。

class Descriptor:
    def __get__(self, instance, owner):
        if not owner.instance:
            # 由於Singleton的__new__已被重寫會拋出異常,所以採用object.__new__來實例化
            owner.instance = object.__new__(owner)

        return owner.instance

    def __set__(self, instance, value):
        pass


class Singleton:
    desc = Descriptor()
    instance = None

    def __new__(cls, *args, **kwargs):
        # 實例化的功能實現已經放在了描述符中,所以拋出異常禁止像普通類那樣實例化,只能調用get_instance函數獲得實例
        raise ValueError("can't instantiate, please use 'get_instance'")

    @staticmethod
    def get_instance():
        instance = Singleton.desc # 會調用Descriptor.__get__
        return instance


if __name__ == '__main__':
    a = Singleton.get_instance()
    b = Singleton.get_instance()
    print(id(a), id(b))  # id一樣
    
    c = Singleton()  # 會報錯,只能通過get_instance函數獲得實例

 

 

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