如果该属性在派生类中被覆盖,如何调用基类的属性?
Posted
技术标签:
【中文标题】如果该属性在派生类中被覆盖,如何调用基类的属性?【英文标题】:How to call a property of the base class if this property is being overwritten in the derived class? 【发布时间】:2010-11-04 12:31:14 【问题描述】:我正在将我的一些类从广泛使用 getter 和 setter 更改为更 Pythonic 使用属性。
但现在我被卡住了,因为我以前的一些 getter 或 setter 会调用基类的相应方法,然后执行其他操作。但是如何通过属性来实现呢?如何调用父类中的属性getter或setter?
当然只是调用属性本身会产生无限递归。
class Foo(object):
@property
def bar(self):
return 5
@bar.setter
def bar(self, a):
print a
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return self.bar # --> recursion!
@bar.setter
def bar(self, c):
# perform the same action
# as in the base class
self.bar = c # --> recursion!
# then do something else
print 'something else'
fb = FooBar()
fb.bar = 7
【问题讨论】:
【参考方案1】:您可能认为您可以调用由属性调用的基类函数:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
虽然这是我认为最明显的尝试 - 它不起作用,因为 bar 是一个属性,而不是可调用的。
但是属性只是一个对象,用getter方法来查找对应的属性:
class FooBar(Foo):
@property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
【讨论】:
为什么我应该在继承的 setter 中调用Foo.bar.fset(self, c)
?为什么不Foo.bar.fset(c)
- 没有“自我” - 我认为这是隐含传递的?
我得到一个 TypeError: 'property' object is not callable
@nerdoc 从Foo.bar.fset
链中得到的self
暗示在哪里?
只是一个想法 - AFAIK self 总是被隐式传递。如果您执行 foo = Foo()
\ foo.bar(c)
没有传递 self,但 bar() 从 Python 接收它。我不是 Python 专家,或多或少也是初学者。这只是一个想法。
self
仅在对类的实例调用该方法时才隐式传递。例如,如果我有一个类A
和一个方法b(self, arg)
,并且我创建了一个实例c = A()
,那么调用c.b(arg)
就相当于A.b(c, arg)
【参考方案2】:
Super 应该可以解决问题:
return super().bar
在 Python 2.x 中,您需要使用更详细的语法:
return super(FooBar, self).bar
【讨论】:
TypeError: super() 至少需要 1 个参数(给定 0) 我猜(嗯,从链接判断:P),这个答案是 python3 相关的。在 python3 中 super() 可以接受零个参数,是的。 super().bar 似乎适用于 getter,但不适用于通过覆盖 setter 中的基本属性进行分配。如果我做 super().bar = 3 我得到 AttributeError: 'super' object has no attribute 'bar' 好点 Rob,不知道。这里有更多信息:***.com/questions/10810369/… 虽然这适用于获取,但无法使用AttributeError
进行设置。【参考方案3】:
还有一个使用 super
的替代方法,它不需要显式引用基类名称。
基类 A:
class A(object):
def __init__(self):
self._prop = None
@property
def prop(self):
return self._prop
@prop.setter
def prop(self, value):
self._prop = value
class B(A):
# we want to extend prop here
pass
在B中,访问父类A的属性getter:
正如其他人已经回答的那样,它是:
super(B, self).prop
或者在 Python 3 中:
super().prop
这会返回属性的 getter 返回的值,而不是 getter 本身,但它足以扩展 getter。
在B中,访问父类A的属性setter:
到目前为止我看到的最好的建议如下:
A.prop.fset(self, value)
我相信这个更好:
super(B, self.__class__).prop.fset(self, value)
在此示例中,两个选项是等效的,但使用 super 的优点是独立于 B
的基类。如果B
继承自同样扩展属性的C
类,则无需更新B
的代码。
B扩展A属性的完整代码:
class B(A):
@property
def prop(self):
value = super(B, self).prop
# do something with / modify value here
return value
@prop.setter
def prop(self, value):
# do something with / modify value here
super(B, self.__class__).prop.fset(self, value)
一个警告:
除非您的属性没有 setter,否则您必须在 B
中同时定义 setter 和 getter,即使您只更改其中一个的行为。
【讨论】:
您好,这很有帮助。我对此有一个后续问题。此设置运行良好;但是,当我为 B 设置 init 以定义其他属性时,它停止工作。有没有办法为 B 设置一个单独的 init ?谢谢 您能否解释一下super(B, self.__class__)
如何与super(class, class)
完全配合使用?它记录在哪里?
我在my answer 中建议对此答案进行一些小调整【参考方案4】:
试试
@property
def bar:
return super(FooBar, self).bar
虽然我不确定python是否支持调用基类属性。属性实际上是一个可调用对象,它使用指定的函数进行设置,然后在类中替换该名称。这很可能意味着没有可用的超级函数。
您总是可以切换语法以使用 property() 函数:
class Foo(object):
def _getbar(self):
return 5
def _setbar(self, a):
print a
bar = property(_getbar, _setbar)
class FooBar(Foo):
def _getbar(self):
# return the same value
# as in the base class
return super(FooBar, self)._getbar()
def bar(self, c):
super(FooBar, self)._setbar(c)
print "Something else"
bar = property(_getbar, _setbar)
fb = FooBar()
fb.bar = 7
【讨论】:
如果您编写基类,这可以正常工作。但是,如果您扩展一个使用相同名称的属性和 getter 的第三方基类呢?【参考方案5】:对Maxime's answer的一些小改进:
使用__class__
避免写入B
。请注意,self.__class__
是 self
的运行时类型,但 __class__
without self
是封闭类定义的名称。 super()
是 super(__class__, self)
的简写。
使用__set__
代替fset
。后者特定于property
s,但前者适用于所有类似属性的对象(描述符)。
class B(A):
@property
def prop(self):
value = super().prop
# do something with / modify value here
return value
@prop.setter
def prop(self, value):
# do something with / modify value here
super(__class__, self.__class__).prop.__set__(self, value)
【讨论】:
我不清楚super(__class__, self)
和super(__class__, self.__class__)
之间的区别,以及为什么我们需要后者来设置祖先类的属性?
从help()
到super
,有两个重载——super(type, obj)
,第一种情况,super(type, type2)
,第二种情况。我们将.prop
作为类属性而不是实例属性访问,这就是需要后者的原因。
super(type(self), type(self)).setter.fset(self, value)
不适用于多重继承。试试我的解决方案duper(super()).setter = value
:gist.github.com/willrazen/bef3fcb26a83dffb6692e5e10d3e67ac
什么情况下不能正常工作?【参考方案6】:
您可以使用以下模板:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
@property
def prop1(self):
return self.__prop1
#setter
@prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
@prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
@property
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
@prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
@prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
注意!所有属性方法必须一起重新定义。如果不想重新定义所有方法,请改用以下模板:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
@property
def prop1(self):
return self.__prop1
#setter
@prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
@prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
@Parent.prop1.getter
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
@Parent.prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
@Parent.prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
【讨论】:
super(Child, Child)
错了,应该是Super(Child, self.__class__)
【参考方案7】:
class Base(object):
def method(self):
print "Base method was called"
class Derived(Base):
def method(self):
super(Derived,self).method()
print "Derived method was called"
d = Derived()
d.method()
(除非我在你的解释中遗漏了什么)
【讨论】:
你是:他在谈论属性,而不是简单的方法以上是关于如果该属性在派生类中被覆盖,如何调用基类的属性?的主要内容,如果未能解决你的问题,请参考以下文章