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.