问题描述
背景
在 Python 中,descriptor 是一个定义 __get__
、__set__
或 __delete__
,有时也定义 __set_name__
的对象。 python中最常用的描述符可能是property(getter,setter,deleter,description)
。属性描述符在调用各自的描述符方法时调用给定的 getter、setter 和 deleter。
有趣的是,函数也是描述符:它们定义了 __get__
,当被调用时,返回一个绑定方法。
描述符用于修改访问对象属性时发生的情况。示例包括限制访问、记录对象访问甚至从数据库动态查找。
问题
我的问题是:如何设计可组合的描述符?
例如:
假设我有一个 Restricted
描述符(仅在满足某种条件时才允许设置和获取)和一个 AccessLog
描述符(每次属性“设置”或“得到”)。我可以设计它们以便在使用它们时组合它们的功能吗?
说我的示例用法如下所示:
class ExampleClient:
# use them combined,preferably In any order
# (and there Could be a special way to combine them,# although functional composition makes the most sense)
foo: Restricted(AccessLog())
bar: AccessLog(Restricted())
# and also use them separately
qux: Restricted()
quo: AccessLog()
我正在寻找一种方法将其变成可重用的模式,以便我可以使任何描述符都可组合。关于如何以pythonic方式做到这一点的任何建议?我将自己尝试一些想法,看看什么是有效的,但我想知道是否已经尝试过这种方法,以及是否有某种“最佳实践”或此类事情的通用方法......
解决方法
你可能可以做到这一点。棘手的部分可能是弄清楚如果您的描述符没有要委托给的“子”描述符,那么它们的默认行为应该是什么。也许你想默认行为像一个普通的实例变量?
class DelegateDescriptor:
def __init__(self,child=None):
self.child = child
self.name = None
def __set_name__(self,owner,name):
self.name = name
if self.child is not None:
try:
self.child.__set_name__(owner,name)
except AttributeError:
pass
def __get__(self,instance,owner=None):
if instance is None:
return self
if self.child is not None:
return self.child.__get__(instance,owner)
try:
return instance.__dict__[self.name] # default behavior,lookup the value
except KeyError:
raise AttributeError
def __set__(self,value):
if self.child is not None:
self.child.__set__(instance,value)
else:
instance.__dict__[self.name] = value # default behavior,store the value
def __delete__(self,instance):
if self.child is not None:
self.child.__delete__(instance)
else:
try:
del instance.__dict__[self.name] # default behavior,remove value
except KeyError:
raise AttributeError
现在,除了存储值或委托给另一个描述符之外,这个描述符实际上不做任何事情。但是,您实际的 Restricted
和 AccessLog
描述符可能能够将其用作基类,并在顶部添加自己的逻辑。错误检查也非常基础,在生产中使用它之前,您可能希望在每个用例中更好地提出正确类型的异常和适当的错误消息。