I have the following code:
class A(object):
def __init__(self):
self.name = "A"
super(A, self).__init__()
def Update(self):
print "Update A"
self.PickTarget()
def PickTarget(self):
print "PickTarget A"
class B(object):
def __init__(self):
self.name = "B"
super(B, self).__init__()
def Update(self):
print "Update B"
self.PickTarget()
def PickTarget(self):
print "PickTarget B"
class C(A, B):
def __init__(self):
super(C, self).__init__()
def Update(self, useA):
if useA:
A.Update(self)
else:
B.Update(self)
c = C()
c.Update(useA = True)
# prints:
# Update A
# PickTarget A
c.Update(useA = False)
# prints:
# Update B
# PickTarget A
Why does calling C.Update
with useA=False
still call into A.PickTarget
? How can I make this work how I want it to work (ie B.Update always calls into B.PickTarget)? I'm sure this has been asked before but my searching turned up nothing - probably because I don't know what to search for.
It's because A
is before B
in C
's base classes.
You need to use B.PickTarget(self)
rather than self.PickTarget()
in B.Update(self)
to get this behavior. Otherwise, switch A
and B
in C
's definition.
Edit:
If the intended behavior is for B
to always call methods in B
and for A
to always call methods in A
, it's correct to use A.method(self)
instead of self.method()
, as the second form doesn't imply that method
is in A
.
You should redesign your classes. A
should have a move method that moves the robot randomly and define it's other basic behavior. B
should be a subclass of A
and should have a move method that calls super(B, self).move()
if it doesn't have a path, and otherwise moves on the path. This is the proper way to override a method on a condition.
Whenever a method of an object is called, python uses the "Method Resolution Order" (MRO) to determine which version of the method to call. In this case, although you have explicitly called A.Update()
, A.Update()
doesn't explicitly call A.PickTarget
. It just calls self.PickTarget()
. Since this is a C
object, that's equivalent to C.PickTarget(self)
. C.PickTarget()
is inherited, and the C
MRO dictates in this case that A.PickTarget
is the version of PickTarget
to use.
You can look at the MRO of C
like this:
>>> C.__mro__
(<class '__main__.C'>, <class 'foo.A'>, <class 'foo.B'>, <type 'object'>)
There's a super-informative article on the MRO here.
Regarding how to get the behavior you want -- well, there are lots of pretty obvious ways, and at the same time, no good ones (that I can think of). I don't think this is really a good design. The main point of multiple inheritance is that you can mix and match orthogonal methods in C
, but you're trying to cram multiple versions of roughly similar methods, two from A
and two from B
, into one class. If you tell us more about what you're doing, maybe we can suggest a better solution. (You're also changing the signature of your method while keeping the same name. That seems dodgy to me as well.)
If you're sure that you want to do this, though, another approach you might consider is name mangling, which is designed exactly for cases like this. This does exactly what you want:
class A(object):
def __init__(self):
self.name = "A"
super(A, self).__init__()
def Update(self):
print "Update A"
self.__PickTarget()
def PickTarget(self):
print "PickTarget A"
__PickTarget = PickTarget
class B(object):
def __init__(self):
self.name = "B"
super(B, self).__init__()
def Update(self):
print "Update B"
self.__PickTarget()
def PickTarget(self):
print "PickTarget B"
__PickTarget = PickTarget
class C(A, B):
def __init__(self):
super(C, self).__init__()
def Update(self, useA):
if useA:
A.Update(self)
else:
B.Update(self)
Output:
>>> from mangling import A, B, C
>>> c = C()
>>> c.Update(useA = True)
Update A
PickTarget A
>>> c.Update(useA = False)
Update B
PickTarget B
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With