60

我有一个带有属性的基类(get 方法),我想在子类中覆盖该属性。我的第一个想法是这样的:

class Foo(object):
    def _get_age(self):
        return 11

    age = property(_get_age)


class Bar(Foo):
    def _get_age(self):
        return 44

这不起作用(子类 bar.age 返回 11)。我找到了一个有效的 lambda 表达式解决方案:

age = property(lambda self: self._get_age())

那么这是使用属性并在子类中覆盖它们的正确解决方案,还是有其他首选方法可以做到这一点?

4

11 回答 11

69

我只是更喜欢重复property()以及@classmethod在覆盖类方法时重复装饰器。

虽然这看起来非常冗长,但至少对于 Python 标准而言,您可能会注意到:

1)对于只读属性,property可以用作装饰器:

class Foo(object):
    @property
    def age(self):
        return 11

class Bar(Foo):
    @property
    def age(self):
        return 44

2) 在 Python 2.6 中,properties 增加了一对方法 setterdeleter可用于将快捷方式应用于通用属性,该快捷方式已可用于只读属性:

class C(object):
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value
于 2008-10-26T10:50:15.660 回答
19

我不同意选择的答案是允许覆盖属性方法的理想方式。如果您希望 getter 和 setter 被覆盖,那么您可以使用 lambda 提供对 self 的访问,例如lambda self: self.<property func>.

这(至少)适用于 Python 2.4 到 3.6 版本。

如果有人知道如何通过使用属性作为装饰器而不是直接调用 property() 来做到这一点,我想听听!

例子:

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    meow = property(fget=lambda self: self._get_meow(),
                    fset=lambda self, value: self._set_meow(value))

这样,可以轻松执行覆盖:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'

以便:

>>> foo = Foo()
>>> bar = Bar()
>>> foo.meow, bar.meow = "meow", "meow"
>>> foo.meow
"meow from a Foo"
>>> bar.meow
"meow from a Foo, altered by a Bar"

我在geek at play上发现了这一点。

于 2013-01-16T00:55:18.477 回答
12

另一种方法来做到这一点,而无需创建任何额外的类。我添加了一个 set 方法来显示如果你只覆盖两者之一,你会做什么:

class Foo(object):
    def _get_age(self):
        return 11

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class Bar(Foo):
    def _get_age(self):
        return 44

    age = property(_get_age, Foo._set_age)

这是一个非常人为的例子,但你应该明白这个想法。

于 2008-11-14T22:52:52.063 回答
7

是的,这就是这样做的方法;属性声明在父类的定义执行时执行,这意味着它只能“看到”父类中存在的方法的版本。因此,当您在子类上重新定义这些方法中的一个或多个时,您需要使用子类的方法版本重新声明该属性。

于 2008-10-26T03:07:11.727 回答
3

可能的解决方法可能如下所示:

class Foo:
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        print('Foo: getting age')
        return self._age

    @age.setter
    def age(self, value):
        print('Foo: setting age')
        self._age = value


class Bar(Foo):
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        return super().age

    @age.setter
    def age(self, value):
        super(Bar, Bar).age.__set__(self, value)

if __name__ == '__main__':
    f = Foo(11)
    print(f.age)
    b = Bar(44)
    print(b.age)

它打印

Foo: setting age
Foo: getting age
11
Foo: setting age
Foo: getting age
44

从 David Beazley 和 Brian K. Jones 的“Python Cookbook”中得到这个想法。在 Debian GNU/Linux 9.11 (stretch) 上使用 Python 3.5.3

于 2020-01-04T19:45:14.097 回答
2

我同意您的解决方案,这似乎是一种即时模板方法。 本文处理您的问题并提供准确的解决方案。

于 2008-10-26T03:01:04.127 回答
2

像这样的东西会起作用

class HackedProperty(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, inst, owner):    
        return getattr(inst, self.f.__name__)()

class Foo(object):
    def _get_age(self):
        return 11
    age = HackedProperty(_get_age)

class Bar(Foo):
    def _get_age(self):
        return 44

print Bar().age
print Foo().age
于 2008-10-26T03:17:07.573 回答
2

@mr-b相同,但带有装饰器。

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    @property
    def meow(self):
        return self._get_meow()
    @meow.setter
    def meow(self, value):
        self._set_meow(value)

这样,可以轻松执行覆盖:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'
于 2016-05-20T21:11:55.917 回答
0

我在子类的父类中设置属性时遇到了问题。以下解决方法扩展了父级的属性,但通过直接调用父级的 _set_age 方法来实现。起皱应该总是正确的。虽然它有点 javathonic。

import threading


class Foo(object):
    def __init__(self):
        self._age = 0

    def _get_age(self):
        return self._age

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class ThreadsafeFoo(Foo):

    def __init__(self):
        super(ThreadsafeFoo, self).__init__()
        self.__lock = threading.Lock()
        self.wrinkled = False

    def _get_age(self):
        with self.__lock:
             return super(ThreadsafeFoo, self).age

    def _set_age(self, value):
        with self.__lock:
            self.wrinkled = True if value > 40 else False
            super(ThreadsafeFoo, self)._set_age(value)

    age = property(_get_age, _set_age)
于 2015-06-02T15:52:52.433 回答
0

我认为Vladimir Zolotykh答案几乎是最佳的。 对于我的理解,一个更好的变体可能是调用超类的构造函数。 导致以下代码:

class Foo:
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        print("Foo: getting age")
        return self._age

    @age.setter
    def age(self, value):
        print("Foo: setting age")
        self._age = value


class Bar(Foo):
    def __init__(self, age):
        super().__init__(age)


if __name__ == "__main__":
    a = Foo(11)
    print(a.age)
    b = Bar(44)
    print(b.age)

使用此解决方案,无需重新实现子类的属性。只需告诉子类,它应该通过使用构造函数表现得像超类。

上面的行导致以下输出

Foo: setting age
Foo: getting age
11
Foo: setting age
Foo: getting age
44

用python3。

于 2021-05-16T16:29:46.597 回答
-1
class Foo:
    # Template method
    @property
    def age(self):
        return self.dothis()
    # Hook method of TM is accessor method of property at here
    def dothis(self):
        return 11
class Bar(Foo):
    def dothis(self):
        return 44

与 Nizam Mohamed 相同,仅提及使用模板方法和属性的样式指南2.13.4

于 2019-08-23T03:51:40.017 回答