Type of an object: ‘obj.__class__’ versus ‘type(obj)’
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Mon Dec 16 17:17:34 EST 2013
On Mon, 16 Dec 2013 12:51:21 +1100, Ben Finney wrote:
> Howdy all,
>
> What is the Pythonic way to determine the type of an object? Are there
> multiple valid ways, and when should each be used?
That is an excellent question, I only wish I had an excellent answer to
give you. Obviously great minds think alike because I was going to ask
the same question, prompted by this comment from Nick Coghlan on the
python-dev list:
"...type(obj).__name__ (working with the concrete type, ignoring any
proxying) or obj.__class__.__name__ (which takes proxying into
account)..."
So there is a difference between them, but I'm not entirely sure what it
is.
> We have ‘obj.__class__’, an attribute bound to the object's class. Or is
> it? When is that true, and when should we not rely on it?
I think you can rely on it. I don't believe you can delete the __class__
attribute from an instance:
py> class X(object):
... pass
...
py> x = X()
py> x.__class__
<class '__main__.X'>
py> del x.__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't delete __class__ attribute
I think it is fair to consider __class__ to be part of the "object API"
shared by all objects. I suppose it's possible to create a metaclass
which does not expose a __class__ attribute, but I would consider that
broken by design.
Furthermore, you can dynamically set the __class__ of an instance in
order to dynamically change its type and therefore behaviour (although
there are restrictions on what you can change it to, and from). Using the
same x instance as above:
py> class Y(object):
... pass
...
py> x.__class__ = Y
py> type(x)
<class '__main__.Y'>
This is by design, and it allows a very useful form of dynamic behaviour:
http://code.activestate.com/recipes/68429-ring-buffer/
> We have ‘type(obj)’, calling the constructor for the ‘type’ type in
> order to get a reference to the type of ‘obj’. Or is it? When is that
> true, and when should we not rely on it?
Are there circumstances where type(obj) and obj.__class__ return
something different? Based on Nick's comment above, I would have to guess
the answer must be yes, but I don't know what those circumstances are.
Aside: I'm not sure that it is useful to think of type as the constructor
in the one-argument form. If you recall, prior to Python 2.2 `type` was a
regular function which took one argument and returned the argument's type:
[steve at ando ~]$ python1.5
Python 1.5.2 (#1, Aug 27 2012, 09:09:18) [GCC 4.1.2 20080704 (Red Hat
4.1.2-52)] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> type
<built-in function type>
The types returned were very different from the types we know and love
since the class/type unification of version 2.2:
>>> type(42)
<type 'int'>
>>> int
<built-in function int>
>>> type(42)('2')
Traceback (innermost last):
File "<stdin>", line 1, in ?
TypeError: call of non-function (type type)
So while it is *technically* correct that type(...) calls the type
constructor, the one-argument form type(obj) is intended to behave as a
function, while the three-argument form type(name, bases, namespace) is
intended to behave as a constructor of types. But I digress.
> Are there other ways to get at the type of a Python object? What reasons
> are there to choose or avoid them?
I am not aware of any other ways.
--
Steven
More information about the Python-list
mailing list