所以,我试图找出最好的(最优雅,代码量最少)的方法来允许在python中覆盖属性的特定函数(例如,只是getter,只是setter等)。我喜欢以下做属性的方法,因为它们的所有方法都被封装在相同的缩进代码块中(更容易看到处理一个属性的函数停止在哪里以及处理函数的函数下一步开始):
@apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
但是,如果我想从以这种方式定义属性的类继承,然后,比如覆盖foo
setter函数,那么它似乎很棘手。我已经做了一些搜索,我发现的大部分答案都是在基类中定义单独的函数(例如getFoo
和setFoo
),从它们显式创建属性定义(例如{ {1}}),然后根据需要覆盖foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())
,getFoo
和setFoo
。
我不喜欢这个解决方案,因为这意味着我必须为每个属性定义lambas,然后写出每个函数调用(在我刚刚完成delFoo
之前)。我也没有得到我原来的封装。
理想情况下,我希望能够做到的是:
property(**locals())
然后输出看起来像:
class A(object):
def __init__(self):
self.foo = 8
@apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
class ATimesTwo(A):
@some_decorator
def foo():
def fset(self, val):
self._foo = val * 2
return something
基本上,>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16
从ATimesTwo
继承getter函数,但会覆盖setter函数。有没有人知道这样做的方式(以类似于上面例子的方式)? A
看起来像什么函数,some_decorator
函数应该返回什么?
答案 0 :(得分:48)
property
装饰器上的Python docs建议使用以下内容:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
然后子类可以覆盖单个setter / getter,如下所示:
class C2(C):
@C.x.getter
def x(self):
return self._x * -1
这有点肮脏,因为覆盖多种方法似乎需要你做一些事情:
class C3(C):
@C.x.getter
def x(self):
return self._x * -1
# C3 now has an x property with a modified getter
# so modify its setter rather than C.x's setter.
@x.setter
def x(self, value):
self._x = value * 2
当然,当你覆盖getter,setter和deleter时,你可以重新定义C3的属性。
答案 1 :(得分:45)
我确定您之前已经听过这个消息,但自从Python 2.3以来,apply
已被弃用八年。不要使用它。您对locals()
的使用也与Python的Zen相反 - 显式优于隐式。如果您真的喜欢增加缩进,则无需创建一次性对象,只需执行
if True:
@property
def foo(self):
return self._foo
@foo.setter
def foo(self, val):
self._foo = val
哪个不滥用locals
,使用apply
,需要创建一个额外的对象,或者之后需要一行foo = foo()
,这样会更难看到块的结尾。它也适用于您使用property
的老式方式 - 正常地执行foo = property(fget, fset)
。
如果要覆盖任意子类中的属性,可以使用recipe like this。
如果子类知道属性的定义位置,只需执行:
class ATimesTwo(A):
@A.foo.setter
def foo(self, val):
self._foo = val * 2
答案 2 :(得分:0)
stderr的答案可以满足大多数用例。
对于要扩展getter
,setter
和/或deleter
的情况,我想添加一个解决方案。做到这一点的两种方法是:
property
执行此操作的第一种方法是将内置property
子类化,并添加装饰器,这些装饰器是 extend <的getter
,setter
和/或deleter
的版本/ em>当前的获取,设置和删除回调
支持将方法追加到set函数的属性的示例:
class ExtendableProperty(property):
def append_setter(self, fset):
# Create a wrapper around the new fset that also calls the current fset
def _appended_setter(obj, value):
self.fset(obj, value)
fset(obj, value)
# Use that wrapper as setter instead of only the new fset
return self.setter(_appended_setter)
用法与普通属性相同,只是现在可以将方法添加到属性设置器中
class A(object):
@ExtendableProperty
def prop(self):
return self._prop
@prop.setter
def prop(self, v):
self._prop = v
class B(A):
@A.prop.append_setter
def prop(self, v):
print('Set', v)
>>> a = A()
>>> a.prop = 1
>>> a.prop
1
>>> b = B()
>>> b.prop = 1
Set 1
>>> b.prop
1
使用常规属性,覆盖getter,setter或Deleter,然后在父类的属性中将调用添加到fget
,fset
或fdel
。
如示例1所示的属性类型示例:
class A(object):
@property
def prop(self):
return self._prop
@prop.setter
def prop(self, v):
self._prop = v
class B(A):
@A.prop.setter
def prop(self, v):
A.prop.fset(self, v) # This is the call to the original set method
print('Set {}'.format(v))
我认为第一个选项看起来更好,因为不需要调用超级属性的fset