Reputation: 21496
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.
Upvotes: 3
Views: 150
Reputation: 1283
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.
Upvotes: 0
Reputation: 24318
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
Upvotes: 1
Reputation: 26407
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.
Upvotes: 1