This article has a snippet showing usage of __bases__
to dynamically change the inheritance hierarchy of some Python code, by adding a class to an existing classes collection of classes from which it inherits. Ok, that's hard to read, code is probably clearer:
class Friendly: def hello(self): print 'Hello' class Person: pass p = Person() Person.__bases__ = (Friendly,) p.hello() # prints "Hello"
That is, Person
doesn't inherit from Friendly
at the source level, but rather this inheritance relation is added dynamically at runtime by modification of the __bases__
attribute of the Person class. However, if you change Friendly
and Person
to be new style classes (by inheriting from object), you get the following error:
TypeError: __bases__ assignment: 'Friendly' deallocator differs from 'object'
A bit of Googling on this seems to indicate some incompatibilities between new-style and old style classes in regards to changing the inheritance hierarchy at runtime. Specifically: "New-style class objects don't support assignment to their bases attribute".
My question, is it possible to make the above Friendly/Person example work using new-style classes in Python 2.7+, possibly by use of the __mro__
attribute?
Disclaimer: I fully realise that this is obscure code. I fully realize that in real production code tricks like this tend to border on unreadable, this is purely a thought experiment, and for funzies to learn something about how Python deals with issues related to multiple inheritance.
Ok, again, this is not something you should normally do, this is for informational purposes only.
Where Python looks for a method on an instance object is determined by the __mro__
attribute of the class which defines that object (the M ethod R esolution O rder attribute). Thus, if we could modify the __mro__
of Person
, we'd get the desired behaviour. Something like:
setattr(Person, '__mro__', (Person, Friendly, object))
The problem is that __mro__
is a readonly attribute, and thus setattr won't work. Maybe if you're a Python guru there's a way around that, but clearly I fall short of guru status as I cannot think of one.
A possible workaround is to simply redefine the class:
def modify_Person_to_be_friendly(): # so that we're modifying the global identifier 'Person' global Person # now just redefine the class using type(), specifying that the new # class should inherit from Friendly and have all attributes from # our old Person class Person = type('Person', (Friendly,), dict(Person.__dict__)) def main(): modify_Person_to_be_friendly() p = Person() p.hello() # works!
What this doesn't do is modify any previously created Person
instances to have the hello()
method. For example (just modifying main()
):
def main(): oldperson = Person() ModifyPersonToBeFriendly() p = Person() p.hello() # works! But: oldperson.hello() # does not
If the details of the type
call aren't clear, then read e-satis' excellent answer on 'What is a metaclass in Python?'.
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