[英]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.