E.Beach
E.Beach

Reputation: 1857

How to compare two classes/types in python?

I have two classes defined in a module classes.py:

class ClassA(object):
    pass

class ClassB(object):
    pass

And in another module I am getting the attributes of the module:

import classes

Class1 = getattr(classes, 'ClassA')
Class2 = getattr(classes, 'ClassA')
print type(Class1) == type(Class2)

Class3 = getattr(classes, 'ClassA')
Class4 = getattr(classes, 'ClassB')
print type(Class3) == type(Class4)

Both type comparison are returning True and that's not what I was expecting.

How can I compare class types using python's native type values?

Upvotes: 14

Views: 42946

Answers (4)

Azat Ibrakov
Azat Ibrakov

Reputation: 11009

If you want to check if types are equal then you should use is operator.

Example: we can create next stupid metaclass

class StupidMetaClass(type):
    def __eq__(self, other):
        return False

and then class based on it:

  • in Python 2

      class StupidClass(object):
          __metaclass__ = StupidMetaClass
    
  • in Python 3

    class StupidClass(metaclass=StupidMetaClass):
         pass
    
    

then a simple check

>>> StupidClass == StupidClass

returns False, while

>>> StupidClass is StupidClass

returns an expected True value.

So as we can see == operator on classes can be overloaded while there is no way (at least simple one) to change is operator's behavior.

Upvotes: 10

vaultah
vaultah

Reputation: 46593

Explanation

This is why your comparison doesn't work as expected

>>> class ClassA(object):
...     pass
... 
>>> class ClassB(object):
...     pass
... 
>>> type(ClassB)
<class 'type'> 
>>> type(ClassA)
<class 'type'> 
>>> type(ClassA) == type(ClassB)
True

But why do ClassA and ClassB have the same type type? Quoting the docs:

By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).

Example:

>>> ClassB
<class '__main__.ClassB'>
>>> type('ClassB', (), {})
<class '__main__.ClassB'>
>>> type(ClassB)
<class 'type'>
>>> type(type('ClassB', (), {}))
<class 'type'>

Getting the type of ClassB is exactly the same as getting the type of type('ClassB', (), {}), which is type.

Solutions

Compare them directly (w/out using the type() function):

>>> ClassA
<class '__main__.ClassA'>
>>> ClassB
<class '__main__.ClassB'>
>>> ClassA == ClassB
False

or initialize them and compare the types of their objects:

>>> a = ClassA()
>>> b = ClassB()
>>> type(a) 
<class '__main__.ClassA'>
>>> type(b) 
<class '__main__.ClassB'>
>>> type(a) == type(b)
False

FWIW you can also use is in place of == (for classes).

Upvotes: 18

Danstahr
Danstahr

Reputation: 4319

In addition to the other answers :

Python uses the concept of metaclasses, which are basically "classes of classes". That means, even a Class is an object in Python, which has its own class - accessible using the type in-build function.

Because ClassA and ClassB are by default instances of the same metaclass, the comparisons return True.

If you'd like to know more about metaclasses, this SO post is a good start.

Upvotes: 1

Wooble
Wooble

Reputation: 90027

You're comparing the type of the class object, which are all of type 'type'.

If you just want to compare the classes, compare them directly:

print Class3 == Class4

Upvotes: 3

Related Questions