In Python 3, types are classes (as far as repr() is concerned)

December 25, 2020

In yesterday's entry, I put in a little aside, saying 'the distinction between what is considered a 'type' and what is considered a 'class' by repr() is somewhat arbitrary'. It turns out that this is not true in Python 3, which exposes an interesting difference between Python 2 and Python 3 and a bit of old Python 1 and Python 2 history too.

(So the sidebar in this old entry of mine is not applicable to Python 3.)

To start with, let's show the situation in Python 2:

>>> class A:
...     pass
>>> class B(object):
...     pass
>>> repr(A)
'<class __main__.A at 0x7fd804cacf30>'
>>> repr(B)
"<class '__main__.B'>"
>>> repr(type)
"<type 'type'>"

Old style and new style classes in Python 2 are reported slightly differently, but they are both 'class', while type (or any other built in type such as int) are 'type'. This distinction is made at a quite low level, as described in the sidebar in my old entry.

However, in Python 3 things have changed and repr()'s output is uniform:

>>> class B(object):
...   pass
>>> repr(B)
"<class '__main__.B'>"
>>> repr(type)
"<class 'type'>"

Both Python classes and built-in types are 'class'. This change was specifically introduced in Python 3, as issue 2565 (the change appeared in 3.0a5). The issue's discussion has a hint as to what was going on here.

To simplify a bit, in Python 1.x, there was no unification between classes and built in types. As part of this difference, their repr() results were different in the way you'd expect; one said 'class' and the other said 'type'. When Python 2.0 came along, it unified types with new style classes. The initial implementation of this unification caused repr() to report new style classes as types. However, at some point relatively early in 2.x development, this code was changed to report new style classes as 'class ...' instead. What was reported for built in types was left unchanged for backwards compatibility with the Python 1.x output of repr(). In the run up to Python 3, this backwards compatibility was removed and now all built in types (or if you prefer, classes) are reported as classes.

(I was going to say something about what type() reports, but then I actually thought about it. In reality type() doesn't report any sort of string; type() returns an object, and if you're just running that in an interactive session the interpreter prints it using str(), which for classes is normally the same as repr(). The reason to use 'repr(B)' instead of 'type(B)' in my interactive example is that 'type(B)' is type.)

Sidebar: The actual commit message for the 2001 era change

issue 2565 doesn't quote the full commit message, and it turns out that the omitted bit is interesting (especially since it's a change made by Guido van Rossum):

Change repr() of a new-style class to say <class 'ClassName'> rather than <type 'ClassName'>. Exception: if it's a built-in type or an extension type, continue to call it <type 'ClassName>. Call me a wimp, but I don't want to break more user code than necessary.

As far as I can tell from reading old Python changelogs, this change appeared in Python 2.2a4. In a way, this is surprisingly late in Python 2.x development. The 'what's new' snippet about the change reiterates that not changing the output for built in types is for backward compatibility:

The repr() of new-style classes has changed; instead of <type 'M.Foo'> a new-style class is now rendered as <class 'M.Foo'>, except for built-in types, which are still rendered as <type 'Foo'> (to avoid upsetting existing code that might parse or otherwise rely on repr() of certain type objects).

Of course, at that point it was also for compatibility with people relying on what repr() of built in types reported in 2.0 and 2.1.

Written on 25 December 2020.
« In CPython, types implemented in C actually are part of the type tree
The expiry time of Certificate Authority root certificates can be nominal (or not) »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Fri Dec 25 00:32:25 2020
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.