[Python-ideas] Adding Type[C] support to PEP 484 and typing.py

Guido van Rossum guido at python.org
Fri May 13 00:35:04 EDT 2016

On Thu, May 12, 2016 at 8:00 PM, Chris Angelico <rosuav at gmail.com> wrote:

> On Fri, May 13, 2016 at 12:52 PM, Guido van Rossum <guido at python.org>
> wrote:
> > Great. We'll be able to move this very quickly once we decide whether it
> > should be called Type[C] or Class[C]. The original proposal is Type[C],
> and
> > it's nice because it's a pun on `type`, just like Tuple[...] is a pun on
> > `tuple` (and similar for List etc.). But OTOH it really annotates a class
> > object, not an abstract type. I still prefer Type[C] -- anyone want to
> argue
> > that we're making a mistake? (I know, nobody's listening, everyone's too
> > busy arguing that Path should inherit from str. :-)
> ISTM Type is the more obvious name here. Python doesn't have class
> objects any more than it has def objects - it has functions and types.

Well, actually, I call them class objects all the time... Plus PEP 484 (at
least the recent revisions, though this has always been the BDFL-delegate's
intention) tries to make a clear terminological between classes (the things
you have at runtime) and types (the things that type checkers care about).

There's a big overlap because most classes are also types -- but not the
other way around! E.g. Any is a type but not a class (you can neither
inherit from Any nor instantiate it), and the same is true for unions and
type variables. And it's pretty clear that when you have an argument or
variable annotated with Type[C] it has to be a real class object, not Any
or a union or a type variable, because in the common use case, functions
taking class objects either instantiate that class, call class methods on
it, or use it for isinstance() checks, and none of those things would work
if at run time they received Any or a union or type variable.

Let's look at that function new_user() again:

def new_user(user_class: Type[User]) -> User:
    return user_class()

We can call new_user(ProUser) or new_user(TeamUser), but calling
new_user(Any) isn't going to work at runtime, not us
new_user(Union[ProUser, TeamUser]).

Note also that this is a unique twist for Type[C] that doesn't exist for
non-type annotations. If we have a function argument annotated with a
class, e.g.

def first_name(u: User) -> str: ...

it's totally reasonable to call it with an argument whose type is
Union[ProUser, TeamUser]:

def get_user_by_id(id: str) -> Union[ProUser, TeamUser]: ...
name = first_name(get_user_by_id('joe'))

This is because here the *type* returned by get_user_by_id() is a union,
but the actual *values* it returns are always instances of one of the
runtime classes ProUser or TeamUser.

If we had had more consistent terminology in Python from the start, this
naming issue would never have come up -- we'd never have used type(x) as
(almost) equivalent to x.__class__, and we wouldn't have used class
C(type): ... to define a metaclass (i.e. a class's class).

But that's water under the bridge, and we might a well write Type[C] rather
than Class[C] to pub with `type`. And it seems only two people care about
the difference -- Mark Shannon and myself. :-) So Type[C] it is.

--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160512/7682931d/attachment.html>

More information about the Python-ideas mailing list