我在写多个类,在一堆属性上加了一个共同的逻辑。这是一个简化版的代码。
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
并稍作修改,就像你的答案所证明的那样,为了代码的重用,你可以将 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
我的意思不是要复制 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中重复。
感谢@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