3

I have a four distinct classes. There is a main base/parent class, two main classes that inherit from this parent class, and another class that inherits from both of these main classes. If I have a method with the same name but a different number of arguments as a parent class, I get a TypeError.

# Example

class Parent(object):
    def check(self, arg):
        tmp = {
            'one': False,
            'two': False
        }

        try:
            if 'one' in arg:
                tmp['one'] = True

            if 'two' in arg:
                tmp['two'] = True
        except TypeError:
            pass

        return tmp

class Child(Parent):
    def check(self, arg):
        return Parent.check(self, arg)['one']

    def method(self, arg):
        if self.check(arg):
            print 'One!'

class ChildTwo(Parent):
    def check(self, arg):
        return Parent.check(self, arg)['two']

    def method(self, arg):
        if self.check(arg):
            print 'Two!'

class ChildThree(Child, ChildTwo):
    def check(self, arg, arg2):
        print arg2
        return Child.check(self, arg)

    def method(self, arg):
        if self.check(arg, 'test'):
            print 'One!'

        ChildTwo.method(self, arg)

test = ChildThree()
test = test.method('one and two')

runfile('untitled6.py', wdir='./Documents')
test
One!
Traceback (most recent call last):
File "< stdin >", line 1, in < module >
File "C:\Users\py\AppData\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 714, in runfile
execfile(filename, namespace)
File "C:\Users\py\AppData\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 74, in execfile
exec(compile(scripttext, filename, 'exec'), glob, loc)
File "untitled6.py", line 49, in
test = test.method('one and two')
File "untitled6.py", line 46, in method
ChildTwo.method(self, arg)
File "untitled6.py", line 34, in method
if self.check(arg):

TypeError: check() takes exactly 3 arguments (2 given)

However, when I remove the second argument from the 'check' method in 'ChildThree', it seems to work fine:

class ChildThree(Child, ChildTwo):
    def check(self, arg):
        return Child.check(self, arg)

    def method(self, arg):
        if self.check(arg):
            print 'One!'

        ChildTwo.method(self, arg)

runfile('untitled6.py', wdir='./Documents')
One!
Two!

I am fairly new to classes/inheritance, so I am not sure why an extra argument causes a TypeError even though it calls the parent class method with a single argument.

2 Answers 2

2

Consider this line:

ChildTwo.method(self, arg)

You passed in self explicitly. self here is a reference to a ChildThree instance. Later, in the body of ChildTwo.method:

if self.check(arg):

It's the same self we're talking about here; self is still a reference on your ChildThree instance.

It looks like you expected self to do something magical, but it doesn't - it's just a plain old name. For it to refer to a ChildTwo instance it would have had to be called like a bound method. Compare and contrast:

  • my_child_two.method(arg) <-- "self" gets passed implicitly by descriptor protocol
  • ChildTwo.method(self, arg) <-- "self" is just whatever it is
Sign up to request clarification or add additional context in comments.

2 Comments

Ah, that makes sense! I'll accept the answer once it allows me to (says to wait 3 mins). Is there a good/Pythonic way to fix this, or would I just change 'if self.check(arg):' in the body of 'ChildTwo.method' to 'if ChildTwo.check(self, arg)'?
The way I would recommend is to avoid having methods with the same name and different argument specs, in inheritance situations. That just sounds needlessly complicated and a large part of "pythonic" is simplicity.
2

This type of inheritance is called "The Diamond Problem". It is a topic for itself, so I'll explain on a simpler case:

class C1(object):
    def check(self, arg):
        return 1

    def method(self, arg):
        return self.check(arg)

class C2(C1):
    def check(self, arg1, arg2):   # this overrides C1.check!
        return x + C1.check(self, arg1)

c2 = C2()
c2.method(55)   # fails

C2.check overrides C1.check on all C2 instances. Therefore, when self.check(arg) is called from method, it calls C2.check for instances of C2. That will fail because C2.check takes two arguments.

How to resolve that? When overriding methods, do not change their signature (number and type of received arguments and type of return value), or you'll get in trouble.

[more advanced] You could have more freedom with functions which take *args and **kwargs.


Besides that, I see that ChildThree.check calls Child.check which calls Parent.check, but noone calls ChildTwo.check. That cannot be right. You should either call the method on all base classes (and risk calling the Parent implementation twice, which may even be right here), or use super().

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.