I have two classes, Field
and Background
. They look a little bit like this:
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()
:
"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.
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.
I expected behavior like c++ where a class can override a method that was inherited. How can i achieve this or something similar.
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.
So far, so good.
Ah. This is where we get the error.
Even though this line occurs within
Field.__init__
,self
is an instance ofBackground
. soself.buildField
findsBackground
'sbuildField
method, notField
's.Since
Background.buildField
expects 2 arguments instead of 1,raises an error.
So how do we tell Python to call
Field
'sbuildField
method instead ofBackground
's?The purpose of name mangling (naming an attribute with double underscores) is to solve this exact problem.
The method name
__buildField
is "mangled" to_Field__buildField
insideField
so insideField.__init__
,calls
self._Field__buildField()
, which isField
's__buildField
method. While similarly,inside
Background.__init__
callsBackground
's__buildField
method.Overriding
is talked about but it sounds like to mechaining constructors or (methods)
And also it sounds like over-writing properties:
Let me explain:
A property named field will be initialized as
[0,0,0]
.@property
decorators looks better fit.Then,
Background
class over-write this property.Quick and Dirty Solution
I do not know your business logic but sometimes by-passing super class's
__init__
method gave me more control:Using properties
Has more clean syntax.
The
super(Background, self).__init__( a, b )
will invoke:in
Field
. However,self
here refers to thebackground
instance, andself.buildField()
is in fact callingbuildField()
ofBackground
, which is why you get that error.It seems to be that your code should be better written as:
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 usingclassmethod
decorator orstaticmethod
, if you have to call these methods in your constructor.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.
Actually
Background init()
is getting called..But take a look at your Background class..
So, the first statement of
__init__
is invoking thesuper class(Field)
init method.. and passing theself
as argument.. Now thisself
is actually a reference ofBackground class
..Now in your Field class: -
Your
buildField()
method is actually invoking the one in the Background class.. This is because, theself
here is instance ofBackground
class(Try printingself.__class__
in your__init__
method ofField class
).. As you passed it while invoking the__init__
method, fromBackground
class..That's why you are getting error..
As you are not passing any value.. So, only value passed is the implicit
self
.Coming from a C++ perspective, there might be two misconceptions here.
First, overriding a method with a 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.
The second issue is that if a method defined in the superclass calls buildField, the subclass version will be called. In python, all methods are bound dynamically, like a C++
virtual
method.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.The thing with
super
is 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.