I want to test whether an object is an instance of a class, and only this class (no subclasses). I could do it either with:
obj.__class__ == Foo obj.__class__ is Foo type(obj) == Foo type(obj) is Foo
Are there reasons to choose one over another? (performance differences, pitfalls, etc)
In other words: a) is there any practical difference between using __class__
and type(x)
? b) are class objects always safe for comparison using is
?
Update: Thanks all for the feedback. I'm still puzzled by whether or not class objects are singletons, my common sense says they are, but it's been really hard to get a confirmation (try googling for "python", "class" and "unique" or "singleton").
I'd also like to clarify that, for my particular needs, the "cheaper" solution that just works is the best, since I'm trying to optimize the most out of a few, specialized classes (almost reaching the point where the sensible thing to do is to drop Python and develop that particular module in C). But the reason behind the question was to understand better the language, since some of its features are a bit too obscure for me to find that information easily. That's why I'm letting the discussion extend a little instead of settling for __class__ is
, so I can hear the opinion of more experienced people. So far it's been very fruitful!
I ran a small test to benchmark the performance of the 4 alternatives. The profiler results were:
Python PyPy (4x) type() is 2.138 2.594 __class__ is 2.185 2.437 type() == 2.213 2.625 __class__ == 2.271 2.453
Unsurprisingly, is
performed better than ==
for all cases. type()
performed better in Python (2% faster) and __class__
performed better in PyPy (6% faster). Interesting to note that __class__ ==
performed better in PyPy than type() is
.
Update 2: many people don't seem to understand what I mean with "a class is a singleton", so I'll ilustrate with an example:
>>> class Foo(object): pass ... >>> X = Foo >>> class Foo(object): pass ... >>> X == Foo False >>> isinstance(X(), Foo) False >>> isinstance(Foo(), X) False >>> x = type('Foo', (object,), dict()) >>> y = type('Foo', (object,), dict()) >>> x == y False >>> isinstance(x(), y) False >>> y = copy.copy(x) >>> x == y True >>> x is y True >>> isinstance(x(), y) True >>> y = copy.deepcopy(x) >>> x == y True >>> x is y True >>> isinstance(x(), y) True
It doesn't matter if there are N objects of type type
, given an object, only one will be its class, hence it's safe to compare for reference in this case. And since reference comparison will always be cheaper than value comparison, I wanted to know whether or not my assertion above holds. I'm reaching the conclusion that it does, unless someone presents evidence in contrary.
Python type() is a built-in function that returns the type of the objects/data elements stored in any data type or returns a new type object depending on the arguments passed to the function. The Python type() function prints what type of data structures are used to store the data elements in a program.
__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .
type() returns the type of the object you put in as an argument, and is usually not useful unless compared with a real type (such as type(9) == int ). isinstance() returns a boolean - true or false - based on whether the object is of given type.
Conclusions. isinstance is usually the preferred way to compare types. It's not only faster but also considers inheritance, which is often the desired behavior. In Python, you usually want to check if a given object behaves like a string or a list, not necessarily if it's exactly a string.
For old-style classes, there is a difference:
>>> class X: pass ... >>> type(X) <type 'classobj'> >>> X.__class__ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: class X has no attribute '__class__' >>> x = X() >>> x.__class__ <class __main__.X at 0x171b5d50> >>> type(x) <type 'instance'>
The point of new-style classes was to unify class and type. Technically speaking, __class__
is the only solution that will work both for new and old-style class instances, but it will also throw an exception on old-style class objects themselves. You can call type()
on any object, but not every object has __class__
. Also, you can muck with __class__
in a way you can't muck with type()
.
>>> class Z(object): ... def __getattribute__(self, name): ... return "ham" ... >>> z = Z() >>> z.__class__ 'ham' >>> type(z) <class '__main__.Z'>
Personally, I usually have an environment with new-style classes only, and as a matter of style prefer to use type()
as I generally prefer built-in functions when they exist to using magic attributes. For example, I would also prefer bool(x)
to x.__nonzero__()
.
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