This question already has answers here:
How does Python's super() work with multiple inheritance?
(18 answers)
Closed 7 years ago.
when searching for attributes, Python looks in object g 1st. If I number each class named in the drawing below (2=2nd, 3=3rd, etc.) Could anyone show me the order in which all these classes are searched.
class A : pass
class B : pass
class C(A,object): pass
class D: pass
class E(D,B): pass
class F(B,C): pass
class G(E,F): pass
g = G()
This order:
>>> G.__mro__
(<class '__main__.G'>, <class __main__.E at 0x028412D0>, <class __main__.D at 0x
02841298>, <class '__main__.F'>, <class __main__.B at 0x02841260>, <class '__mai
n__.C'>, <class __main__.A at 0x02841228>, <type 'object'>)
>>> print ', '.join(klass.__name__ for klass in G.__mro__)
G, E, D, F, B, C, A, object
If you want to know how that order is computed, there isn't a good, short answer. The incomplete, short answer is that Python ensures that subclasses come before superclasses and classes earlier in a list of base classes come before classes later in a list of base classes. The long answer is the 40-page document about C3 linearization on python.org, although the actual algorithm isn't 40 pages.
Related
This question already has answers here:
How to access the type arguments of typing.Generic?
(5 answers)
Closed 19 days ago.
class MyClass:
prop: list[str]
MyClass.__annotations__
# {'prop': list[str]}
How do I access "str"?
As a more generic question, given an obnoxiously complex and long type hint, like prop: list[set[list[list[str] | set[int]]]], how do I access the internal values programmatically?
Here is a function that will recursively pretty print the type and all the types inside it, using typing.get_args as suggested by #user2357112 :
from typing import List, Set, Union
import typing
complex_type = Union[List[str], Set[int]]
def log_type_layers(typing_type, indent=4, depth=0):
current_type = typing_type
print(" "*indent*depth + str(current_type))
for subtype in typing.get_args(current_type):
log_type_layers(subtype, indent=indent, depth=depth+1)
log_type_layers(complex_type)
Output:
typing.Union[typing.List[str], typing.Set[int]]
typing.List[str]
<class 'str'>
typing.Set[int]
<class 'int'>
So I've done a bit of research online and it seems like the __subclasses__ method returns all the inherited classes for a python object (relevant stack overflow question)
On python3.8 I then tried the following:
class A:
a = 1
class B:
b = 2
class C(A, B):
c = 3
obj = C()
print('a: ', obj.a)
print('subclasses: ', C.__subclasses__())
and I get out
a: 1
subclasses: []
this shows that class C successfully inherits A and B, however they don't show up with the subclasses method? So is there something I'm missing in the __subclasses__ method, or has the method changed for python 3.8?
just combining the answerts from above :
my_class.__subclasses__ will return the classes, which subclass from my_class
C.__mro__ shows the inheritence hierarchy in your case :
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
object
/ \
A B
\ /
C
In short, __subclasses__ goes down the object hierarchy ladder and the __mro__ goes up. Good luck :)
This question already has answers here:
Understanding class type '__main__.ClassName'
(2 answers)
Closed 2 years ago.
I'm creating a class and checking the arguments for __init__.
class Example:
def __init__(self, a):
for key, val in locals().items():
print(type(val))
When I create this class, (something like Example(14)) I get <class '__main__.Example'> on the first line.
I know this is 'self', but what type is it?
The __main__ prefix is present because your class is defined in your script's global scope.
>>> class A:
... pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>>
The type of Example(14) is... well, Example, since that's the class of which that object is an instance.
Why after searching B, it does not go deeper to search Y OR z but go to search A?
Y is the parent of A, if should search A first, but Y is the parent of B so it should search Y first, why this does not throw a MRO error?
Can someone explain how this lookup works?
class X(object):pass
class Y(object): pass
class Z(object): pass
class A(X,Y): pass
class B(Y,Z):pass
class M(B,A,Z):pass
print M.__mro__
gives
(<class '__main__.M'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.X'>, <class '__main__.Y'>, <class '__main__.Z'>, <type 'object'>)
In your specific example, after searching B, we can't consider Y immediately because it is a child of A. We can't consider Z immediately because M inherits from A before it inherits from Z.
Python uses C3 method resolution order details here .
C3 resolution order solves the diamond inheritance problem well
In the example below, we have a very generic class Object that's a superclass of B and C. We only want method implementations (say of __repr__ or something) in Object to be considered if neither B nor C have an implementation.
Object
/ \
B C
\ /
A
In other words, each possible parent in the transitive closure of the parent classes of A is considered, but the classes are ordered according to the "latest" path from the base class to the class in question.
There are two paths to object:
A -> B -> Object
A -> C -> Object
The "latest" path is A -> C -> Object because A -> B -> Object would be earlier in a left-biased depth-first search.
C3 linearization satisfies two key invariants:
if X inherits from Y, X is checked before Y.
if Z inherits from U and then V in that order, U is checked before V.
Indeed C3 linearization guarantees that both of those properties hold.
It's possible to construct hierarchies that can't be linearized, in which case you get an exception at class definition time.
running inherit.py
class E: pass
class F: pass
class A(E, F): pass
class B(F, E): pass
class Z(A, B): pass
produces the following error.
Traceback (most recent call last):
File "inherit.py", line 5, in <module>
class Z(A, B): pass
TypeError: Cannot create a consistent method resolution
order (MRO) for bases E, F
import pickle
class A:
pass
pickle.dumps(B().__reduce__())
yields
(<function _reconstructor at 0x1010143b0>, (<class '__main__.B'>, <class 'object'>, None))
What is this function "_reconstructor". It's neither B, B.__init__, nor B.__new__ as I expected.
I have to make 2 changes to get that result:
Change the name of your class from A to B.
Remove the outer pickle.dumps() call.
In any case, pickle is free to do anything it likes to reconstruct the object ;-) In this case, you can find the _reconstructor() function in Lib/copyreg.py.