在 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
来实现这一点?
感谢@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
的参考代码并进行少量修改会如您的答案所示那样工作,但为了代码重用,您可以将 class 子 property
class 并调用 super()
来访问父 class 的方法。
此外,您实现中的 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')
这样您就可以在编写各种 类 时避免使用样板文件 ,而不是在各种 getters/setters 中重复使用各种 类。
我正在编写多个 类,并在一堆属性中添加了通用逻辑。这是代码的简化版本:
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
来实现这一点?
感谢@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
的参考代码并进行少量修改会如您的答案所示那样工作,但为了代码重用,您可以将 class 子 property
class 并调用 super()
来访问父 class 的方法。
此外,您实现中的 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')
这样您就可以在编写各种 类 时避免使用样板文件 ,而不是在各种 getters/setters 中重复使用各种 类。