3

I am writing a class which should subclass, amongst other classes, code.InteractiveInterpreter. For some reason, one of the methods that that class normally has (compile) is not available on its subclasses when you use multiple inheritance.

Single inheritance works fine:

>>> from code import InteractiveInterpreter
>>> class Single(InteractiveInterpreter): pass
...
>>> hasattr(Single(), 'compile')
True

Multiple inheritance does not:

>>> class Double(object, InteractiveInterpreter): pass
...
>>> hasattr(Double(), 'compile')
False

Flipped the order around though, it works:

>>> class Flipped(InteractiveInterpreter, object): pass
...
>>> hasattr(Flipped(), 'compile')
True

Is there some subtle detail of multiple inheritance that I'm unaware of that is preventing compile from being inherited in some cases, or is there a bug in Python causing this (given the name of the method in question is also the name of a built-in function, I feel like this might be possible.)

I'm trying to reproduce the issue with a class other than InteractiveInterpreter but am unable to... this works fine:

>>> class Original():
...     def compile(self): pass
...
>>> class Secondary(object, Original): pass
...
>>> hasattr(Secondary(), 'compile')
True

I'm using Python 2.7.11, 32 bit, on Windows 10.

3 Answers 3

1

Are you sure Flipped produced the given results?

I get the following with a similar setup,

>>> from code import InteractiveInterpreter
>>> class Flipped(InteractiveInterpreter, object): pass
... 
>>> hasattr(Flipped(), 'compile')
True

Based on the source of the module, compile is not a method of the class but an instance attribute created on object initialization. It would make sense that inheriting from object first would not provide the attribute because it's __init__ is defined and the subclass doesn't call InteractiveInterpreter.__init__ to assign the attribute.

Sign up to request clarification or add additional context in comments.

4 Comments

I had a typo when I tried. I mistakenly did hasattr(Flipped, 'compile') (missing (), so checking the instance instead). I've updated the question to mention that that actually works.
This still isn't clear to me - why isn't the __init__ method for InteractiveInterpreter being called if the bases are arranged the other way in the class definition? Does object.__init__ not call super?
It would appear that object.__init__ does not call super... further, it only seems to be a problem if anything listed after it in bases is an old style class (as InteractiveInterpreter is).
@ArtOfWarfare Python will follow MRO for method calls and will stop when it finds a superclass providing the method.
1

I don't exactly know where the problem lies, but one workaround is to explicitly call the constructor of InteractiveInterpreter, where the compile method is actually defined:

class Double(object, InteractiveInterpreter):
    def __init__(self, *args, **kwargs):
        InteractiveInterpreter.__init__(self, *args, **kwargs)

Note that a simply calling to super(Double, self).__init__(...) does not suffice (at least in my environment). However, this works for me

>>> hasattr(Flipped(), 'compile')
True

My environment: Python 2.7.11 (default, Jan 5 2016, 12:49:55) [GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin

Comments

0

It seems like something when you mix object and an old style class

Python 2.7.6 on Ubuntu 14.04

I checked the code for code.InteractiveInterpreter (it's using an old style class), then did some testing: (I just imported code, then found the file via code.__file__ )

class Klass:
    def __init__(self):
        self.compile = 1

class Klass2(object):
    def __init__(self):
        self.compile = 1

class BlankObj:
    pass

class BlankObj2(object):
    pass


class Frist(object, Klass):
    pass

class Second(BlankObj, Klass):
    pass

class Thrid(BlankObj, Klass2):
    pass

class Fourth(BlankObj, Klass2):
    pass

Then played around with those objects:

>>> from testing import Frist, Second, Thrid, Fourth
>>> hasattr(Frist(), 'compile')
False
>>> hasattr(Second(), 'compile')
True
>>> hasattr(Thrid(), 'compile')
True
>>> hasattr(Fourth(), 'compile')
True
>>> 
>>> f = Frist()
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> s = Second()
>>> dir(s)
['__doc__', '__init__', '__module__', 'compile']

Attempting to make a 'Fifth' class, where I inherit from object and Klass2 causes this traceback:

>>> class Fifth(object, Klass2):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases object, Klass2

I'm not sure why this is happening, but it's definitely related to mixing object directly with an old style class.

1 Comment

It seems to me the issue is that object doesn't call super(object, self).__init__, so InteractiveInterpreter's __init__ method never gets called. As Jared pointed out, compile isn't a proper method, but instead it's a function that gets attached as an instance variable within InteractiveInterpreter.__init__. Not sure why they chose to implement it that way.

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.