一、什麼是描述符
在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函數獲得實例