簡體   English   中英

在 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來實現這一點?

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

這樣,您在編寫各種類時就可以避免樣板,而不是在各種類的各種 getter/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的參考代碼並進行少量修改,但為了代碼重用,您可以將property class 子類化並調用super()來訪問父 class 的方法。

此外,您的實現中的setter function 不必要地實例化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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM