一、什么是描述符
在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函数获得实例
本文介绍了Python中的描述符概念,通过实现__get__/__set__/__delete__方法来反向控制类的行为。接着,文章利用描述符的特性详细讲解了如何实现单例模式,将单例实例化过程放在描述符的__get__方法中,并通过重写Singleton类的__new__方法防止直接实例化,确保只能通过特定方法获取单例实例。内容简要演示了实现过程,但未涉及线程安全的实现。
686

被折叠的 条评论
为什么被折叠?



