Python,覆盖继承的类方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12764995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python, Overriding an inherited class method
提问by d00nut
I have two classes, Fieldand Background. They look a little bit like this:
我有两个班级,Field和Background. 它们看起来有点像这样:
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
def buildField( self, c ):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
This error is pointing to Field's buildField():
此错误指向 Field 的buildField():
"TypeError: buildField() takes exactly 2 arguments (1 given)."
I expected Background init() to be called first. To pass "a, b" to Fields init(), Field to assign a and b then to assign a list with three 0's in it to field. Then for Background's init() to continue, to then call its own buildField() and override self.field with a list containing c.
我希望首先调用Background init()。将 "a, b" 传递给 Fields init(),Field 分配 a 和 b,然后将其中包含三个 0 的列表分配给 field。然后让后台的init() 继续,然后调用它自己的 buildField() 并用包含 c 的列表覆盖 self.field。
It seems I don't fully understand super(), however i was unable to find a solution to my issue after looking at similar inheritance problems on the web and around here.
似乎我并不完全理解 super(),但是在查看网络上和这里的类似继承问题后,我无法找到解决我的问题的方法。
I expected behavior like c++ where a class can override a method that was inherited. How can i achieve this or something similar.
我期望像 c++ 这样的行为,其中类可以覆盖继承的方法。我怎样才能实现这个或类似的东西。
Most issues I found related to this were people using double underscores. My experience with inheritance with super is using the inherited class init() to just pass different variables to the super class. Nothing involving overwriting anything.
我发现与此相关的大多数问题是人们使用双下划线。我使用 super 继承的经验是使用继承的类init() 将不同的变量传递给超类。没有涉及覆盖任何内容。
采纳答案by Ian Clelland
Coming from a C++ perspective, there might be two misconceptions here.
从 C++ 的角度来看,这里可能有两个误解。
First, a method with the same name and different signature does not overload it like in C++. If one of your Background objects tries to call buildField with no arguments, the original version from Field will not be called -- it has been completely hidden.
首先,具有相同名称和不同签名的方法不会像在 C++ 中那样重载它。如果您的 Background 对象之一尝试不带参数调用 buildField,则不会调用 Field 中的原始版本——它已完全隐藏。
The second issue is that if a method defined in the superclass calls buildField, the subclass version will be called. In python, allmethods are bound dynamically, like a C++ virtualmethod.
第二个问题是,如果超类中定义的方法调用buildField,则将调用子类版本。在 python 中,所有方法都是动态绑定的,就像 C++virtual方法一样。
Field's __init__expected to be dealing with an object that had a buildField method taking no arguments. You used the method with an object that has a buildField method taking one argument.
Field__init__预计将处理一个具有不带参数的 buildField 方法的对象。您将该方法用于具有带一个参数的 buildField 方法的对象。
The thing with superis that it doesnt change the type of the object, so you shouldn't change the signature of any methods that the superclass' methods might call.
问题super是它不会改变对象的类型,所以你不应该改变超类方法可能调用的任何方法的签名。
回答by Rohit Jain
I expected Background init() to be called
我希望调用 Background init()
Actually Background init()is getting called..
实际上Background init()是被调用..
But take a look at your Background class..
但是看看你的背景课..
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
So, the first statement of __init__is invoking the super class(Field)init method.. and passing the selfas argument.. Now this selfis actually a reference of Background class..
所以, 的第一条语句__init__是调用super class(Field)init 方法.. 并传递selfas 参数.. 现在这self实际上是Background class..的引用。
Now in your Field class: -
现在在您的 Field 类中:-
class Field( object ):
def __init__( self, a, b ):
print self.__class__ // Prints `<class '__main__.Background'>`
self.a = a
self.b = b
self.field = self.buildField()
Your buildField()method is actually invoking the one in the Background class.. This is because, the selfhere is instance of Backgroundclass(Try printing self.__class__in your __init__method of Field class).. As you passed it while invoking the __init__method, from Backgroundclass..
您的buildField()方法实际上是在调用 Background 类中的一个。这是因为,self这里是Background类的实例(尝试self.__class__在您的__init__方法中打印Field class)。当您在调用__init__方法时传递它时,从Background类..
That's why you are getting error..
这就是为什么你得到错误..
The error "TypeError: buildField() takes exactly 2 arguments (1 given).
错误“TypeError: buildField() 需要 2 个参数(给定 1 个)。
As you are not passing any value.. So, only value passed is the implicit self.
由于您没有传递任何值。因此,传递的唯一值是隐式self.
回答by K Z
The super(Background, self).__init__( a, b )will invoke:
该super(Background, self).__init__( a, b )会调用:
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
in Field. However, selfhere refers to the backgroundinstance, and self.buildField()is in fact calling buildField()of Background, which is why you get that error.
在Field。然而,self这里指的是background实例,self.buildField()实际上是在呼吁buildField()的Background,这就是为什么你得到这个错误。
It seems to be that your code should be better written as:
似乎您的代码应该更好地编写为:
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = Field.buildField()
@classmethod
def buildField(cls):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__(a, b)
self.field = Background.buildField(c)
@classmethod
def buildField(cls,c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
If you can't allow the base constructor to finish then it signals that the design is flawed.
如果您不能让基础构造函数完成,则表明设计存在缺陷。
It is therefore much better to separate buildField()to belong to the class by using classmethoddecoratoror staticmethod, if you have tocall these methods in your constructor.
因此,如果您必须在构造函数中调用这些方法,最好buildField()使用classmethod装饰器或来分离属于该类。staticmethod
However, if your base class constructor does not invoke any instance method from within, you can then safely overwrite any method of this base class.
但是,如果您的基类构造函数没有从内部调用任何实例方法,则您可以安全地覆盖此基类的任何方法。
回答by unutbu
I expected Background init() to be called. To pass "a, b" to Fields init(), Field to assign a and b
我希望调用 Background init()。将 "a, b" 传递给 Fields init(),Field 分配 a 和 b
So far, so good.
到现在为止还挺好。
then to assign a list with three 0's in it to field.
然后将其中包含三个 0 的列表分配给字段。
Ah. This is where we get the error.
啊。这就是我们得到错误的地方。
self.field = self.buildField()
Even though this line occurs within Field.__init__, selfis an instance of Background. so self.buildFieldfinds Background's buildFieldmethod, not Field's.
即使此行出现在 内Field.__init__,self也是 的一个实例Background。所以self.buildFieldfindBackground的buildField方法,而不是Field的。
Since Background.buildFieldexpects 2 arguments instead of 1,
由于Background.buildField期望 2 个参数而不是 1 个,
self.field = self.buildField()
raises an error.
引发错误。
So how do we tell Python to call Field's buildFieldmethod instead of Background's?
那么我们如何告诉 Python 调用Field'sbuildField方法而不是Background's ?
The purpose of name mangling(naming an attribute with double underscores) is to solve this exact problem.
目的名称重整(命名有双下划线的属性)是解决这一确切的问题。
class Field(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.field = self.__buildField()
def __buildField(self):
field = [0,0,0]
return field
class Background(Field):
def __init__(self, a, b, c):
super(Background, self).__init__(a, b)
self.field = self.__buildField(c)
def __buildField(self, c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background(a, b, c)
The method name __buildFieldis "mangled" to _Field__buildFieldinside Fieldso inside Field.__init__,
方法名称__buildField被“破坏”到_Field__buildField里面Field所以里面Field.__init__,
self.field = self.__buildField()
calls self._Field__buildField(), which is Field's __buildFieldmethod. While similarly,
调用self._Field__buildField(),这是Field的__buildField方法。虽然类似,
self.field = self.__buildField(c)
inside Background.__init__calls Background's __buildFieldmethod.
内部Background.__init__调用Background的__buildField方法。
回答by guneysus
Overridingis talked about but it sounds like to me chaining constructors or (methods)
Overriding被谈论,但对我来说听起来像 chaining constructors or (methods)
And also it sounds like over-writingproperties:
而且这听起来像是覆盖属性:
Let me explain:
让我解释:
A property named fieldwill be initialized as
[0,0,0].@propertydecorators looks better fit.Then,
Backgroundclass over-writethis property.
名为field的属性将被初始化为
[0,0,0].@property装饰器看起来更合适。然后,
Background类覆盖此属性。
Quick and Dirty Solution
快速而肮脏的解决方案
I do not know your business logic but sometimes by-passing super class's __init__method gave me more control:
我不知道你的业务逻辑,但有时绕过超类的__init__方法给了我更多的控制权:
#!/usr/bin/env python
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
# super(Background, self).__init__( a, b )
# Unfortunately you should repeat or move initializing a and b
# properties here
self.a = a
self.b = b
self.field = self.buildField( c )
def buildField( self, c ):
# You can access super class methods
assert super(Background, self).buildField() == [0,0,0]
field = [c]
return field
a, b, c = 0, 1, 2
bg = Background(a,b,c)
assert bg.field == [2]
Using properties
使用属性
Has more clean syntax.
有更干净的语法。
#!/usr/bin/env python
class Field( object ):
@property
def field(self):
return [0,0,0]
def __init__( self, a, b ):
self.a = a
self.b = b
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.c = c
assert (self.a, self.b, self.c) == (0,1,2) # We assigned a and b in
# super class's __init__ method
assert super(Background, self).field == [0,0,0]
assert self.field == [2]
@property
def field(self):
return [self.c]
a, b, c = 0, 1, 2
background = Background( a, b, c )
print background.field

