[英]Overriding properties in python
So, I'm trying to figure out the best (most elegant with the least amount of code) way to allow overriding specific functions of a property (eg, just the getter, just the setter, etc.) in python.所以,我试图找出最好的(用最少的代码最优雅)的方法来允许覆盖 python 中属性的特定功能(例如,只是 getter,只是 setter 等)。 I'm a fan of the following way of doing properties, due to the fact that all of their methods are encapsulated in the same indented block of code (it's easier to see where the functions dealing with one property stop and the functions dealing with the next begin):
我喜欢以下处理属性的方式,因为它们的所有方法都封装在同一个缩进的代码块中(更容易看出处理一个属性的函数在哪里停止以及处理下一个开始):
@apply
def foo():
"""A foobar"""
def fget(self):
return self._foo
def fset(self, val):
self._foo = val
return property(**locals())
However, if I want to inherit from a class that defines properties in this manner, and then, say, override the foo
setter function, it seems tricky.但是,如果我想从以这种方式定义属性的 class 继承,然后,比如说,覆盖
foo
设置器 function,这似乎很棘手。 I've done some searching and most of the answers I've found have been to define separate functions in the base class (eg getFoo
and setFoo
), explicitly create a property definition from them (eg foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())
), and then override getFoo
, setFoo
, and delFoo
as needed.我进行了一些搜索,发现的大多数答案都是在基础 class 中定义单独的函数(例如
getFoo
和setFoo
),从它们显式创建属性定义(例如foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())
),然后根据需要覆盖getFoo
、 setFoo
和delFoo
。
I dislike this solution because it means I have to define lambas for every single property, and then write out each function call (when before I could have just done property(**locals())
).我不喜欢这个解决方案,因为这意味着我必须为每个属性定义lambas,然后写出每个 function 调用(之前我可以刚刚完成
property(**locals())
)。 I also don't get the encapsulation that I had originally.我也没有得到我最初的封装。
Ideally, what I would like to be able to do would be something like this:理想情况下,我希望能够做的是这样的:
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
And then the output would look something like:然后 output 看起来像:
>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16
Basically, ATimesTwo
inherits the getter function from A
but overrides the setter function.基本上,
ATimesTwo
从A
继承了 getter function,但覆盖了 setter function。 Does anybody know of a way to do this (in a manner that looks similar to the example above)?有谁知道这样做的方法(以类似于上面示例的方式)? What function would the
some_decorator
look like, and what should the foo
function return? some_decorator 会是什么样
some_decorator
会是什么样子, foo
function 应该返回什么?
The Python docs on the property
decorator suggest the following idiom: property
装饰器上的 Python文档建议以下成语:
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
And then subclasses can override a single setter/getter like this:然后子类可以像这样覆盖单个 setter/getter:
class C2(C):
@C.x.getter
def x(self):
return self._x * -1
This is a little warty because overriding multiple methods seems to require you to do something like:这有点棘手,因为覆盖多个方法似乎需要您执行以下操作:
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
Of course at the point that you're overriding getter, setter, and deleter you can probably just redefine the property for C3.当然,当您覆盖 getter、setter 和 deleter 时,您可能只需重新定义 C3 的属性。
I'm sure you've heard this before, but apply
has been deprecated for eight years , since Python 2.3.我敢肯定你以前听过这个,但是自从 Python 2.3 以来,
apply
已经被弃用了八年。 Don't use it.不要使用它。 Your use of
locals()
is also contrary to the Zen of Python -- explicit is better than implicit.您对
locals()
的使用也与 Python 的禅宗相反——显式优于隐式。 If you really like the increased indentation, there is no need to create a throwaway object, just do如果您真的喜欢增加的缩进,则无需创建一次性的 object,只需执行
if True:
@property
def foo(self):
return self._foo
@foo.setter
def foo(self, val):
self._foo = val
Which doesn't abuse locals
, use apply
, require creation of an extra object, or need a line afterwards with foo = foo()
making it harder to see the end of the block.这不会滥用
locals
,使用apply
,需要创建一个额外的 object ,或者之后需要一行foo = foo()
使得更难看到块的末尾。 It works just as well for your old-fashioned way of using property
-- just do foo = property(fget, fset)
as normal.它同样适用于您使用
property
的老式方式 - 只需像往常一样执行foo = property(fget, fset)
。
If you want to override a property in an arbitrary subclass, you can use a recipe like this .如果要覆盖任意子类中的属性,可以使用这样的配方。
If the subclass knows where the property was defined, just do:如果子类知道属性的定义位置,只需执行以下操作:
class ATimesTwo(A):
@A.foo.setter
def foo(self, val):
self._foo = val * 2
The answer of stderr satisfies most use cases. stderr 的答案满足了大多数用例。
I'd like to add a solution for the case where you want to extend a getter
, setter
and/or deleter
.我想为您想要扩展
getter
、 setter
和/或deleter
的情况添加一个解决方案。 Two ways to do this are:有两种方法可以做到这一点:
property
property
First way to do this is by subclassing the builtin property
and adding decorators that are versions of getter
, setter
and/or deleter
that extend the current get, set and delete callbacks第一种方法是继承内置
property
并添加装饰器,这些装饰器是getter
、 setter
和/或deleter
的版本,它们扩展了当前的 get、set 和 delete 回调
Example for a property that supports appending methods to the set-functions:支持将方法附加到集合函数的属性示例:
class ExtendableProperty(property):
def append_setter(self, fset):
# Create a wrapper around the new fset that also calls the current fset
_old_fset = self.fset
def _appended_setter(obj, value):
_old_fset(obj, value)
fset(obj, value)
# Use that wrapper as setter instead of only the new fset
return self.setter(_appended_setter)
Usage is the same as for normal properties, only now it is possible to add methods to the property setters:用法与普通属性相同,只是现在可以向属性设置器添加方法:
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
Use a normal property, overwrite the getter, setter or deleter and then add calls to the fget
, fset
or fdel
in the property of the parent class.使用普通属性,覆盖 getter、setter 或 deleter,然后在父 class 的属性中添加对
fget
、 fset
或fdel
的调用。
Example for the type of property as in example 1:示例 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))
I think the first option looks nicer because the call to the super property's fset is not necessary我认为第一个选项看起来更好,因为不需要调用超级属性的 fset
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.