在 Python 中扩展 @property.setter 装饰器

Extending @property.setter decorator in Python

我正在编写多个 类,并在一堆属性中添加了通用逻辑。这是代码的简化版本:

class FooAspect:
  _bar_prop = 'bar'

  def __init__(self, bar_value: int):
    self._bar = bar_value

  @property
  def bar(self) -> int:
    return self._bar

  @bar.setter
  def bar(self, value: int) -> None:
    self._bar = value

    perfrom_action(self._bar_prop, value)

perform_action 总是有相似的形式,我想用装饰器封装它。本质上我正在寻找一种方法来写这样的东西:

# ... define @my_setter

class FooAspect:
  # ...

  @property
  def bar(self) -> int:
    return self._bar

  @bar.my_setter(key='bar')
  def bar(self, value: int) -> None:
    self._bar = value

是否可以扩展 @property@prop.setter 来实现这一点?

感谢@juanpa.arrivillaga,我想出了这个解决方案来实现我自己的描述符:

# myproperty.py

class MyProperty:
    def __init__(self, fget=None, fset=None, key=None):
        self.fget = fget
        self.fset = fset
        self.key = key

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")

        perfrom_action(self.key, value)

        self.fset(obj, value)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.key)

    def _setter(self, fset):
        return type(self)(self.fget, fset, self.key)

    def setter(self, key):
        return type(self)(self.fget, self.fset, key=key)._setter
# foo_aspect.py

from myproperty import MyProperty

class FooAspect:
  # ...

  @MyProperty
  def bar(self) -> int:
    return self._bar

  @bar.setter(key='bar')
  def bar(self, value: int) -> None:
    self._bar = value

虽然复制和粘贴 property 的参考代码并进行少量修改会如您的答案所示那样工作,但为了代码重用,您可以将 class 子 property class 并调用 super() 来访问父 class 的方法。

此外,您实现中的 setter 函数不必要地实例化 MyProperty 的新实例,而它可以通过返回 self._setter:

来重用当前对象
class MyProperty(property):
    def __set__(self, obj, value):
        super().__set__(obj, value)
        perform_action(self.key, value)

    def _setter(self, fset):
        obj = super().setter(fset)
        obj.key = self.key
        return obj

    def setter(self, key):
        self.key = key
        return self._setter

这样:

class FooAspect:
    @MyProperty
    def bar(self) -> int:
        return self._bar

    @bar.setter(key='bar')
    def bar(self, value: int) -> None:
        self._bar = value

def perform_action(key, value):
    print(key, value)

f = FooAspect()
f.bar = 'foo'

输出:

bar foo

我的意思不是要复制 property 实现,那是 故意非常通用 ,如果您的逻辑始终相同,则可能不需要为了你的 getter 和 setter。所以最好做这样的事情:

def perform_action(key, value):
    print(key, value)

class PerformKeyAction:
    def __init__(self, key):
        self.key = key

    def __get__(self, instance, owner):
        # maybe common getter behavior
        return getattr(instance, self.attr_name)

    def __set__(self, instance, value):
        perform_action(self.key, value)
        return setattr(instance, self.attr_name, value)

    def __set_name__(self, owner, name):
        self.attr_name = f'_{name}'

class FooAspect:
    bar = PerformAction(key='bar')

class BazAspect:
    buzz = PerformAction(key='buzz_key')

class FizzAspect:
    fang = PerformAction(key='fang_key')

这样您就可以在编写各种 类 时避免使用样板文件 ,而不是在各种 getters/setters 中重复使用各种 类。