覆盖python中的属性

Jes*_*ick 41 python inheritance overriding properties

所以,我试图找出最好的(最优雅,代码最少)的方法来允许在python中覆盖属性的特定函数(例如,只是getter,只是setter等).我喜欢以下做属性的方法,因为它们的所有方法都被封装在相同的缩进代码块中(更容易看到处理一个属性的函数停止在哪里以及处理函数的函数下一步开始):

@apply
def foo():
    """A foobar"""
    def fget(self):
        return self._foo
    def fset(self, val):
        self._foo = val
    return property(**locals())
Run Code Online (Sandbox Code Playgroud)

但是,如果我想从以这种方式定义属性的类继承,然后,比如重写foosetter函数,那么它似乎很棘手.我做了一些搜索和大多数我已经找到了答案已经确定在基类(如独立的功能getFoosetFoo),明确从他们(例如,创建一个属性定义foo = property(lambda x: x.getFoo(), lambda x, y: x.setFoo(y), lambda x: x.delFoo())),然后覆盖getFoo,setFoo以及delFoo根据需要.

我不喜欢这个解决方案,因为这意味着我必须为每个属性定义lambas,然后写出每个函数调用(在我刚刚完成之前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
Run Code Online (Sandbox Code Playgroud)

然后输出看起来像:

>>> a = A()
>>> a.foo
8
>>> b = ATimesTwo()
>>> b.foo
16
Run Code Online (Sandbox Code Playgroud)

基本上,ATimesTwo继承getter函数A但覆盖setter函数.有没有人知道这样做的方式(以类似于上面例子的方式)?some_decorator看起来会是什么函数,foo函数应该返回什么?

std*_*err 51

装饰器上的Python 文档property提出了以下习语:

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
Run Code Online (Sandbox Code Playgroud)

然后子类可以覆盖单个setter/getter,如下所示:

class C2(C):
    @C.x.getter
    def x(self):
        return self._x * -1
Run Code Online (Sandbox Code Playgroud)

这有点吵,因为覆盖多种方法似乎需要你做以下事情:

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
Run Code Online (Sandbox Code Playgroud)

当然,当你重写getter,setter和deleter时,你可以重新定义C3的属性.

  • 非常感谢与x.setter关联的Cxgetter,在其他任何地方都找不到此技巧,这绝对是应该更改的东西,因为这令人困惑!它需要洞察我没有的财产对象。+1,这应该是公认的答案,因为它是最完整的答案。 (2认同)

agf*_*agf 46

我相信你以前听过这个,但自从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
Run Code Online (Sandbox Code Playgroud)

哪个不滥用locals,使用apply,需要创建一个额外的对象,或者之后需要一行,foo = foo()使得更难以看到块的结尾.它适用于您的老式使用方式property- 只需foo = property(fget, fset)照常进行.

如果要覆盖任意子类中的属性,可以使用这样的配方.

如果子类知道属性的定义位置,那么只需:

class ATimesTwo(A):
    @A.foo.setter
    def foo(self, val):
        self._foo = val * 2
Run Code Online (Sandbox Code Playgroud)

  • "`如果是的话:``?? (5认同)
  • @Jean-FrançoisCorbett:这只是一种能够缩进紧随其后的代码块而几乎没有或没有其他副作用的方法。 (2认同)

jus*_*usx 12

stderr 的答案满足大多数用例。

我想为您想要扩展getter,setter和/或 的情况添加一个解决方案deleter。有两种方法可以做到这一点:

1. 子类property

第一种方法是通过子类化内置函数property并添加装饰器,这些装饰器是getter,setter和/或deleter扩展当前 get、set 和 delete 回调的版本

支持将方法附加到集合函数的属性示例:

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)
Run Code Online (Sandbox Code Playgroud)

用法与普通属性相同,只是现在可以向属性设置器添加方法:

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)
Run Code Online (Sandbox Code Playgroud)
>>> a = A()
>>> a.prop = 1
>>> a.prop
1

>>> b = B()
>>> b.prop = 1
Set 1
>>> b.prop
1
Run Code Online (Sandbox Code Playgroud)

2. 覆盖 getter、setter 和/或删除器

使用普通属性,覆盖 getter、setter 或 deleter,然后在父类的属性中添加对fgetfset或 的调用。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))
Run Code Online (Sandbox Code Playgroud)

我认为第一个选项看起来更好,因为不需要调用超级属性的 fset