在Python中扩展@property.setter decorator。

问题描述 投票:0回答:1

我在写多个类,在一堆属性上加了一个共同的逻辑。这是一个简化版的代码。

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 来实现?

python decorator python-decorators
1个回答
1
投票

当复制并粘贴参考代码的时候,我们可以看到 property 并稍作修改,就像你的答案所证明的那样,为了代码的重用,你可以将 property 类和调用 super() 来代替访问父类的方法。

此外, 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

2
投票

我的意思不是要复制 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')

这样你就可以避免那些模板 编写各门课程时 而不是在不同类的不同getterssetters中重复。


1
投票

感谢@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

© www.soinside.com 2019 - 2024. All rights reserved.