diff --git a/pep-0484.txt b/pep-0484.txt index 93900e152..677eb4dcf 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -861,6 +861,101 @@ allow all operations on it, and a value of type ``Any`` can be assigned to a variable (or used as a return value) of a more constrained type. +The type of class objects +------------------------- + +Sometimes you want to talk about class objects, in particular class +objects that inherit from a given class. This can be spelled as +``Type[C]`` where ``C`` is a class. To clarify: while ``C`` (when +used as an annotation) refers to instances of class ``C``, ``Type[C]`` +refers to *subclasses* of ``C``. (This is a similar distinction as +between ``object`` and ``type``.) + +For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + +And suppose we have a function that creates an instance of one of +these classes if you pass it a class object:: + + def new_user(user_class): + user = user_class() + # (Here we could write the user object to a database) + return user + +Without ``Type[]`` the best we could do to annotate ``new_user()`` +would be:: + + def new_user(user_class: type) -> User: + ... + +However using ``Type[]`` and a type variable with an upper bound we +can do much better:: + + U = TypeVar('U', bound=User) + def new_user(user_class: Type[U]) -> U: + ... + +Now when we call ``new_user()`` with a specific subclass of ``User`` a +type checker will infer the correct type of the result:: + + joe = new_user(BasicUser) # Inferred type is BasicUser + +The value corresponding to ``Type[C]`` must be an actual class object +that's a subtype of ``C``, not a special form. IOW, in the above +example calling e.g. ``new_user(Union[BasicUser, ProUser])`` is +rejected by the type checker (in addition to failing at runtime +because you can't instantiate a union). + +Note that it is legal to use a union of classes as the parameter for +``Type[]``, as in:: + + def new_non_team_user(user_class: Type[Union[BasicUser, ProUser]]): + ... + +However the actual argument passed in at runtime must still be a +concrete class object, e.g. in the above example:: + + new_non_team_user(ProUser) # OK + new_non_team_user(TeamUser) # Disallowed by type checker + +``Type[Any]`` is also supported (see below for its meaning). However, +other special constructs like ``Tuple`` or ``Callable`` are not +allowed. + +There are some concerns with this feature: for example when +``new_user()`` calls ``user_class()`` this implies that all subclasses +of ``User`` must support this in their constructor signature. However +this is not unique to ``Type[]``: class methods have similar concerns. +A type checker ought to flag violations of such assumptions, but by +default constructor calls that match the constructor signature in the +indicated base class (``User`` in the example above) should be +allowed. A program containing a complex or extensible class hierarchy +might also handle this by using a factory class method. A future +revision of this PEP may introduce better ways of dealing with these +concerns. + +When ``Type`` is parameterized it requires exactly one parameter. +Plain ``Type`` without brackets is equivalent to ``Type[Any]`` and +this in turn is equivalent to ``type`` (the root of Python's metaclass +hierarchy). This equivalence also motivates the name, ``Type``, as +opposed to alternatives like ``Class`` or ``SubType``, which were +proposed while this feature was under discussion; this is similar to +the relationship between e.g. ``List`` and ``list``. + +Regarding the behavior of ``Type[Any]`` (or ``Type`` or ``type``), +accessing attributes of a variable with this type only provides +attributes and methods defined by ``type`` (for example, +``__repr__()`` and ``__mro__``). Such a variable can be called with +arbitrary arguments, and the return type is ``Any``. + +``Type[T]`` should be considered covariant, since for a concrete class +``C``, ``Type[C]`` matches ``C`` and any of its subclasses. + + Version and platform checking -----------------------------