简体   繁体   English

覆盖 python 中的属性

[英]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 中定义单独的函数(例如getFoosetFoo ),从它们显式创建属性定义(例如foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo()) ),然后根据需要覆盖getFoosetFoodelFoo

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.基本上, ATimesTwoA继承了 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 .我想为您想要扩展gettersetter和/或deleter的情况添加一个解决方案。 Two ways to do this are:有两种方法可以做到这一点:

1. Subclass property 1.子类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并添加装饰器,这些装饰器是gettersetter和/或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

2. Overwrite getter, setter and/or deleter 2.覆盖getter、setter和/或deleter

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 的属性中添加对fgetfsetfdel的调用。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM