Python: check the order of classes in multiple inheritance - python

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

Related

Python type() not giving exact class type instead gives metaclass type

I am trying to pass the type of a class to a method so that it can be dynamically instantiated. The class extends to a base class which further extends to an abstract class. Now when I check the type of my class it comes as the abstract class type instead of the child class.
Here is how my classes look like
class AMeta(type):
# stuff
class Parent(six.with_metaclass(AMeta, object)):
# stuff
class Child(Parent):
# stuff
Now when I use type(Child) or Child.__class__ it gives me AMeta whereas I would like to get Child. I want to pass this Child to another method which would dynamically create its object.
def create_obj(clzz):
return clzz()
When I call the method like create_obj(type(Child)) it doesn't work and breaks but when I call Child.mro()[0] it works fine what is happening here and is there another way to achieve what I am achieving via mro method?
A class is an instance of its metaclass. Ergo:
The type of Child is AMeta
The type of a Child() is Child
If you do type(Child), you are asking what is the type of your Child class. Keep in mind that classes are also instances in Python. When in your script you do class Child..., there is a new name (Child) added to the script's namespace (pretty much a variable called Child of type AMeta since you're specifying that AMeta is the metaclass of Child. Otherwise, it would be of type type which is a bit like the "default" metaclass)
See:
import six
class AMeta(type):
pass
class Parent(six.with_metaclass(AMeta, object)):
pass
class Child(Parent):
pass
print(type(Child))
c=Child()
print(type(c))
In the first print, you get <class '__main__.AMeta'> because you're asking What is the type of my Child instance?. In the second print you get <class '__main__.Child'> because you're asking What is the type of my c instance?
You don't need to do type(Child) to get the class. You can use it directly. For instance:
obj = Child
dynamic_instance = obj()
print(type(dynamic_instance))
Will print <class '__main__.Child'>
Closer to you example, that'd be:
def create_obj(clzz):
return clzz()
a = create_obj(Child)
print("Just created: %s" % type(a))
Which outputs Just created: <class '__main__.Child'>

Method resolution order and metaclasses

How come a metaclass does not show up in the MRO?
For instance:
>>> class Foo(type):
... foo = 21
...
>>> class Bar(metaclass=Foo):
... pass
...
>>> Bar.mro()
[<class '__main__.Bar'>, <class 'object'>]
>>> Bar.foo
21
Also, I saw elsewhere that Python uses C3 linearization to compute the MRO, but this linearization does not handle metaclasses. So what algorithm does Python use in that case?
Because none of your classes derive from the metaclass. A metaclass is not a base class. The metaclass is the factory that produced the Bar class object, just like a class object produces instances.
There is no linearisation required here. Bar is an object of type Foo, just like other classes are of type type. Any subclasses of Bar will have the same type (the metaclass); they have that relationship directly. A class can have only one metaclass at a time.
A metaclass comes last when looking up attributes; so Bar.spam will first be looked for in the MRO, and only then on type(Bar).
Of course, metaclasses use an inheritance hierarchy too; Foo was derived from type in your example. That relationship uses an MRO too.

super(type,subclass) in simple singleton implementation

when I was implementing naive singleton in python, I came up with a problem with super key word. As usual the behavior of super is always tricky and buggy, hope someone can shed light on it. Thanks :)
The problem is that:
class Singleton(object):
def __new__(cls,*args,**kw):
if not hasattr(cls,'_instance'):
#create a instance of type cls,
origin=super(Singleton,Singleton).__new__(cls,*args,**kw)
cls._instance=origin
return cls._instance
class B(Singleton):
def __init__(self,b):
self.b=b
It actually works, but I am wondering
Will it be better if I change line 5 to the below, like in most of the books?
origin=super(Singleton,cls).__new__(cls,*args,**ks)
what's the difference to make?
super() searches the MRO of the current object for the next class that has the requested attribute. The first argument to super() determines the starting point for the MRO search, the second argument determines the object from which to take the MRO.
As long as you don't use multiple inheritance, your MRO will be straightforward. Singleton will always be in the same location in the MRO in that case. With multiple inheritance, where Singleton appears in the MRO of the subclass will differ, and you really want to use cls to get the current MRO, not the MRO of just Singleton.
For your simple example, ignoring cls (e.g. B) is fine:
>>> class Singleton(object):
... def __new__(cls,*args,**kw):
... if not hasattr(cls,'_instance'):
... #create a instance of type cls,
... origin=super(Singleton,Singleton).__new__(cls,*args,**kw)
... cls._instance=origin
... return cls._instance
...
>>> class B(Singleton):
... def __init__(self,b):
... self.b=b
...
>>> B.__mro__
(<class '__main__.B'>, <class '__main__.Singleton'>, <type 'object'>)
>>> Singleton.__mro__
(<class '__main__.Singleton'>, <type 'object'>)
So super(Singleton, Singleton) and super(Singleton, cls) end up searching through the same sublist, finding only object.
Use multiple inheritance however, and you'll get a very different MRO; note that Foo is listed between Singleton and object:
>>> class Foo(object): pass
...
>>> class Bar(Singleton, Foo): pass
...
>>> Bar.__mro__
(<class '__main__.Bar'>, <class '__main__.Singleton'>, <class '__main__.Foo'>, <type 'object'>)
If you use super(Singleton, Singleton), then Foo is inadvertently skipped when looking for inherited methods. super(Singleton, cls) will search through (Foo, object), super(Singleton, Singleton) will only look at object.

Python inheritance, metaclasses and type() function

I can't understand why the following code behaves a particular way, which is described below:
from abc import ABCMeta
class PackageClass(object):
__metaclass__ = ABCMeta
class MyClass1(PackageClass):
pass
MyClass2 = type('MyClass2', (PackageClass, ), {})
print MyClass1
print MyClass2
>>> <class '__main__.MyClass1'>
>>> <class 'abc.MyClass2'>
Why does repr(MyClass2) says abc.MyClass2 (which is by the way not true)?
Thank you!
The problem stems from the fact that ABCMeta overrides __new__ and calls its superclass constructor (type()) there. type() derives the __module__ for the new class from its calling context1; in this case, the type call appears to come from the abc module. Hence, the new class has __module__ set to abc (since type() has no way of knowing that the actual class construction took place in __main__).
The easy way around is to just set __module__ yourself after creating the type:
MyClass2 = type('MyClass2', (PackageClass, ), {})
MyClass2.__module__ = __name__
I would also recommend filing a bug report.
Related: Base metaclass overriding __new__ generates classes with a wrong __module__, Weird inheritance with metaclasses
1: type is a type object defined in C. Its new method uses the current global __name__ as the __module__, unless it calls a metaclass constructor.

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