Inheritance Networks in Python - python

I am new to python and I am trying to draw the inheritance network for a number of classes. I want to number each class in the drawing showing in what order the class objects are searched for attributes (1=first, 2=second, etc.) in object g = G(). Here is what am trying to simulate:
class A : pass
class C : pass
class B(A,C) : pass
class D(A) : pass
class E(B,C) : pass
class F(C) : pass
class G(D,E,F): pass
I went and made the following network. Is there any way to improve upon it, and have I done it this correctly?

Your diagram is almost right as far as showing the inheritance relationships between the classes you've shown. The arrow from B to E should run in the other direction, but everything else is right.
As for numbering the classes in the diagram based on the order they'll be checked in a lookup on an instance of class G, I suggest calling G.mro(). This will give you a list of the classes in the order they'll be searched.
Here's what I get for your example classes:
[<class '__main__.G'>,
<class '__main__.D'>,
<class '__main__.E'>,
<class '__main__.B'>,
<class '__main__.A'>,
<class '__main__.F'>,
<class '__main__.C'>,
<class 'object'>]
If you want to understand how this order is found, you should read up about the C3 linearization algorithm that Python uses. A good place to start is this article written about the new algorithm when it was introduced in Python 2.3.

Related

Python: show type inheritance

I'm trying to look under the hood in idle to wrap my head around python custom classes and how they are stored in memory. Suppose I have the following code:
class Point:
pass
x=Point()
print(x)
Given the following output:
<__main__.Point object at 0x000002A3A071DF60>
I know that since my class consists of no code, when I create an object of type Point, an object of type object is implicitly created from which the Point object x inherits such methods as __str__ etc. However, I cant seem to see the connection ie. when I type dir(x), I dont see any attribute that stores a reference to an object of type object. Am I misunderstanding how it works or is there some attribute that I am unaware of?
There is no (direct) instance of type object involved. When you call Point(), a Point instance is created, and nothing else. Inheritance is a mechanism that operates on classes, not on instances.
Python objects have two properties: Their type and their value. The type of x is Point. The value of x is represented by its __dict__, which is empty (because it has no attributes):
>>> type(x)
<class '__main__.Point'>
>>> x.__dict__
{}
There is no reference to an object instance, because no such instance exists. The inheritance from object manifests in the class Point:
>>> Point.__base__
<class 'object'>
>>> Point.mro()
[<class '__main__.Point'>, <class 'object'>]
So the reason why x can use object methods is because Point inherits from object, not because there's a hidden object instance stored in x. You can see that Point has access to all object methods:
>>> Point.__str__ is object.__str__
True
>>> Point.__init__ is object.__init__
True
# etc...
So, by proxy, x has access to all object methods as well.
you may be looking for Point.__mro__? the method resolution order of the class Point. it tells you in what order python will go looking for methods if you call x.method. in your case this will be:
print(Point.__mro__) # or Point.mro()
# (<class '__main__.Point'>, <class 'object'>)

Method resolution order (__mro__) not available in Python3.x?

I wonder about that it seems like that the method resolution order attribute mro is not available in Python 3.x anymore. Using Python 3.5.2 and creating a random class object instance A, built-in or self-implemented, for example
A = dict()
and then calling
A.__mro__
doesnt work, raising
AttributeError: 'A' object has no attribute '__mro__'
I tried to use A.mro(), but this gives the same type of exception.
How do I receive the method resolution order now?
That doesn't work on instances. The error hints that the variable A in question is not bound to a type, but to an instance of class A. types (classes) very much do have __mro__:
>>> int.__mro__
(<class 'int'>, <class 'object'>)
>>> class Foo():
... pass
...
>>> Foo.__mro__
(<class '__main__.Foo'>, <class 'object'>)
Since A is an instance of a type and not the type itself, you can use type(A).__mro__.
Additionally, your code wouldn't have worked in Python 2 either; no, instances didn't have __mro__ there either.

How super() implements in python programming [duplicate]

This question already has answers here:
How does Python's super() work with multiple inheritance?
(18 answers)
Closed 9 years ago.
I am new to python programming,
below is the example of parent,child classes,There are two direct super classes (i.e. bases) of C: A and B. A comes before B, so one would naturally think that the super class of C is A. However, A inherits its attribute a from T with value a=0: if super(C,c) was returning the superclass of C, then super(C,c).a would return 0 but its won't?
Could you please help me to understand why its returning 2.why not 0
>>> class T(object):
... a = 0
>>> class A(T):
... pass
>>> class B(T):
... a = 2
>>> class C(A,B):
... pass
>>> c = C()
>>>super(C,c).a
2
Thanks,
Hema
It has to do with Python method resolution order, or MRO. The precise definition of Python's MRO is here: Python 2.3's MRO doc. Edit: this explanation by Guido seems easier to read, and it includes an example exactly like yours.
If you call __mro__ on a class, you'll see the order in which it looks up stuff. In this case:
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.T'>, <type 'object'>)
So there you can see that it goes to B first, and only then to T. The reason is that it would otherwise be impossible to override attributes from T in B in this structure, and that's unlikely to be the desired behaviour.
Personally I just always avoid this diamond-style inheritance pattern, except of course for object which is the base class of every other class.

Python: check the order of classes in multiple inheritance

In my framework I must assure that when a class inherits from ClassA, it must inherit also from ClassB, with ClassB following ClassA (in order to make overridden methods work [in Django forms]). E.g.
class MyClass(ClassA, ClassB) --> correct
class MyClass(ClassA) --> wrong
class MyClass(ClassB, ClassA) --> wrong
is there a way to make ClassA check that it is followed by ClassB (on startup or at runtime)?
Instead of requiring a specific inheritance order, provide a ClassC that uses the right order instead:
class ClassC(ClassA, ClassB):
You can verify base classes with a metaclass, but that is overkill when a simple base class will do. A metaclass on ClassA would be called for subclasses of ClassA as well, then you simply test the bases parameter for the right orderings.
There is a way to do it after defining the classes. It uses mro() method.
If you have something like this
class A(object):
pass
class B(object):
pass
class C(A,B):
pass
If you print the output of mro you get the following.
print(A.mro(), B.mro(), C.mro())
([<class '__main__.A'>, <type 'object'>],
[<class '__main__.B'>, <type 'object'>],
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>])
A way could be:
if C.mro()[1] == A.mro()[0] and C.mro()[2] == B.mro()[0]:
go ahead
else:
raise Exception

How to get the parents of a Python class?

How can I get the parent class(es) of a Python class?
Use the following attribute:
cls.__bases__
From the docs:
The tuple of base classes of a class
object.
Example:
>>> str.__bases__
(<type 'basestring'>,)
Another example:
>>> class A(object):
... pass
...
>>> class B(object):
... pass
...
>>> class C(A, B):
... pass
...
>>> C.__bases__
(<class '__main__.A'>, <class '__main__.B'>)
If you want all the ancestors rather than just the immediate ones, use cls.__mro__.
For versions of Python earlier than 3.5, use inspect.getmro:
import inspect
print inspect.getmro(cls)
Usefully, this gives you all ancestor classes in the "method resolution order" -- i.e. the order in which the ancestors will be checked when resolving a method (or, actually, any other attribute -- methods and other attributes live in the same namespace in Python, after all;-).
The fastest way to get all parents, and in order, is to just use the __mro__ built-in.
For instance, repr(YOUR_CLASS.__mro__).
The following:
import getpass
getpass.GetPassWarning.__mro__
...outputs, in order:
(<class 'getpass.GetPassWarning'>, <type 'exceptions.UserWarning'>, <type 'exceptions.Warning'>, <type 'exceptions.Exception'>, <type 'exceptions.BaseException'>, <type 'object'>)
There you have it. The "best" answer may have more votes but this is so much simpler than some convoluted for loop, looking into __bases__ one class at a time, not to mention when a class extends two or more parent classes. Importing and using inspect just clouds the scope unnecessarily.
New-style classes have an mro method you can call which returns a list of parent classes in method resolution order.
Use bases if you just want to get the parents, use __mro__ (as pointed out by #naught101) for getting the method resolution order (so to know in which order the init's were executed).
Bases (and first getting the class for an existing object):
>>> some_object = "some_text"
>>> some_object.__class__.__bases__
(object,)
For mro in recent Python versions:
>>> some_object = "some_text"
>>> some_object.__class__.__mro__
(str, object)
Obviously, when you already have a class definition, you can just call __mro__ on that directly:
>>> class A(): pass
>>> A.__mro__
(__main__.A, object)
If you want to ensure they all get called, use super at all levels.
If you have a variable and want to get its class and parent classes use type() method which will give class for a variable
val="happy coding"
print(type(val).__mro__)
Output:
(<class 'str'>, <class 'object'>)
This funciton will print the all the classes of an object, while in each step the next object will the left most parent.
def print_root_left(class_):
while True:
print(class_)
# Check there if are no bases then we have reached the root class
if not class_.__bases__:
break
class_=class_.__bases__[0] # use the left most parent
example = "hello"
print_root_left(example.__class__)

Categories

Resources