我有一个基类,它有一个属性(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())
那么,这是使用属性并在子类中覆盖它们的正确解决方案,还是有其他首选方法可以做到这一点?
我只是喜欢重复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中,属性增加了一对方法setter
和deleter
,可用于将已经可用于只读属性的快捷方式应用于常规属性:
class C(object):
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
我不同意所选答案是允许覆盖属性方法的理想方法。 如果您希望 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"
我在极客身上发现了这一点。
另一种方法,无需创建任何其他类。我添加了一个 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)
这是一个非常做作的例子,但你应该明白这个想法。
是的,这是这样做的方法;属性声明在执行父类的定义时执行,这意味着它只能"看到"父类上存在的方法版本。因此,在子类上重新定义一个或多个这些方法时,需要使用子类的方法版本重新声明属性。
可能的解决方法可能如下所示:
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 上使用 Python 3.5.3 (stretch)
这样的东西会起作用
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
与 @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'
我在从子类设置父类的属性时遇到了问题。以下工作循环扩展父项的属性,但通过直接调用父项的_set_age方法来实现。皱纹应该始终是正确的。不过有点爪哇语。
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)
我认为弗拉基米尔·佐洛蒂赫的答案几乎是最佳的><。对于我的不典型来说,一个稍微好一点的变体可能是调用超类的构造函数.
生成以下代码:
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。
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