【问题标题】:Composing descriptors in python在python中编写描述符
【发布时间】:2021-07-22 19:46:30
【问题描述】:

背景

在 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方式执行此操作的任何建议?我将自己尝试一些想法,看看有什么效果,但我想知道这是否已经尝试过,是否有某种“最佳实践”或此类事情的通用方法......

【问题讨论】:

    标签: python python-descriptors


    【解决方案1】:

    你也许可以做到这一点。棘手的部分可能是弄清楚如果您的描述符没有要委托的“子”描述符,它们的默认行为应该是什么。也许你想默认表现得像一个普通的实例变量?

    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, instance, 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
    

    现在,这个描述符实际上除了存储一个值或委托给另一个描述符之外的任何事情。但是,您实际的 RestrictedAccessLog 描述符可能可以将其用作基类,并在顶部添加自己的逻辑。错误检查也是非常基础的,在将其用于生产之前,您可能希望在每个用例中使用适当的错误消息更好地提出正确类型的异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多