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@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 -- https://mail.python.org/mailman/listinfo/python-list