我正在编写多个类,这些类具有添加到一堆属性中的通用逻辑。这是代码的简化版本:
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
来实现这一目标?
答案 0 :(得分: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')
这样,您在编写各种类时就避免了样板,而不是在各种类中的各种getter / setter中重复进行。
答案 1 :(得分: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
答案 2 :(得分:1)
复制和粘贴property
的参考代码并进行较小的修改可以如您的回答所示,但为了代码重用,您可以继承property
类并调用{{1} }来访问父类的方法。
此外,当实现中的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'