覆盖python中的属性

时间:2011-08-11 00:48:06

标签: python inheritance properties override

所以,我试图找出最好的(最优雅,代码量最少)的方法来允许在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函数,那么它似乎很棘手。我已经做了一些搜索,我发现的大部分答案都是在基类中定义单独的函数(例如getFoosetFoo),从它们显式创建属性定义(例如{ {1}}),然后根据需要覆盖foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())getFoosetFoo

我不喜欢这个解决方案,因为这意味着我必须为每个属性定义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函数应该返回什么?

3 个答案:

答案 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的答案可以满足大多数用例。

对于要扩展gettersetter和/或deleter的情况,我想添加一个解决方案。做到这一点的两种方法是:

1。子类property

执行此操作的第一种方法是将内置property子类化,并添加装饰器,这些装饰器是 extend <的gettersetter和/或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

2。覆盖getter,setter和/或Deleter

使用常规属性,覆盖getter,setter或Deleter,然后在父类的属性中将调用添加到fgetfsetfdel

如示例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