[WIP] Semantics of variance in type variables and notation conventions (#68)
* Semantics of variance in type variables and notation conventions * Notation convention for type variables in PEP 8
This commit is contained in:
parent
afebe82ef4
commit
a919235364
13
pep-0008.txt
13
pep-0008.txt
|
@ -829,6 +829,19 @@ Note that there is a separate convention for builtin names: most builtin
|
|||
names are single words (or two words run together), with the CapWords
|
||||
convention used only for exception names and builtin constants.
|
||||
|
||||
Type variable names
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Names of type variables introduced in PEP 484 should normally use CapWords
|
||||
preferring short names: ``T``, ``AnyStr``, ``Num``. It is recommended to add
|
||||
suffixes ``_co`` or ``_contra`` to the variables used to declare covariant
|
||||
or contravariant behavior correspondingly. Examples::
|
||||
|
||||
from typing import TypeVar
|
||||
|
||||
VT_co = TypeVar('VT_co', covariant=True)
|
||||
KT_contra = TypeVar('KT_contra', contravariant=True)
|
||||
|
||||
Exception Names
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
67
pep-0484.txt
67
pep-0484.txt
|
@ -724,31 +724,34 @@ It turns out such an argument acts *contravariantly*, whereas the
|
|||
intuitive answer (which is correct in case the function doesn't mutate
|
||||
its argument!) requires the argument to act *covariantly*. A longer
|
||||
introduction to these concepts can be found on Wikipedia
|
||||
[wiki-variance]_; here we just show how to control a type checker's
|
||||
behavior.
|
||||
[wiki-variance]_ and in PEP 483; here we just show how to control
|
||||
a type checker's behavior.
|
||||
|
||||
By default type variables are considered *invariant*, which means that
|
||||
arguments for arguments annotated with types like ``List[Employee]``
|
||||
must exactly match the type annotation -- no subclasses or
|
||||
By default generic types are considered *invariant* in all type variables,
|
||||
which means that values for variables annotated with types like
|
||||
``List[Employee]`` must exactly match the type annotation -- no subclasses or
|
||||
superclasses of the type parameter (in this example ``Employee``) are
|
||||
allowed.
|
||||
|
||||
To facilitate the declaration of container types where covariant type
|
||||
checking is acceptable, a type variable can be declared using
|
||||
``covariant=True``. For the (rare) case where contravariant behavior
|
||||
is desirable, pass ``contravariant=True``. At most one of these may
|
||||
be passed.
|
||||
To facilitate the declaration of container types where covariant or
|
||||
contravariant type checking is acceptable, type variables accept keyword
|
||||
arguments ``covariant=True`` or ``contravariant=True``. At most one of these
|
||||
may be passed. Generic types defined with such variables are considered
|
||||
covariant or contravariant in the corresponding variable. By convention,
|
||||
it is recommended to use names ending in ``_co`` for type variables
|
||||
defined with ``covariant=True`` and names ending in ``_contra`` for that
|
||||
defined with ``contravariant=True``.
|
||||
|
||||
A typical example involves defining an immutable (or read-only)
|
||||
container class::
|
||||
|
||||
from typing import TypeVar, Generic, Iterable, Iterator
|
||||
|
||||
T = TypeVar('T', covariant=True)
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
|
||||
class ImmutableList(Generic[T]):
|
||||
def __init__(self, items: Iterable[T]) -> None: ...
|
||||
def __iter__(self) -> Iterator[T]: ...
|
||||
class ImmutableList(Generic[T_co]):
|
||||
def __init__(self, items: Iterable[T_co]) -> None: ...
|
||||
def __iter__(self) -> Iterator[T_co]: ...
|
||||
...
|
||||
|
||||
class Employee: ...
|
||||
|
@ -762,16 +765,19 @@ container class::
|
|||
mgrs = ImmutableList([Manager()]) # type: ImmutableList[Manager]
|
||||
dump_employees(mgrs) # OK
|
||||
|
||||
The read-only collection classes in ``typing`` are all defined using a
|
||||
covariant type variable (e.g. ``Mapping`` and ``Sequence``). The
|
||||
The read-only collection classes in ``typing`` are all declared
|
||||
covariant in their type variable (e.g. ``Mapping`` and ``Sequence``). The
|
||||
mutable collection classes (e.g. ``MutableMapping`` and
|
||||
``MutableSequence``) are defined using regular invariant type
|
||||
variables. The one example of a contravariant type variable is the
|
||||
``Generator`` type, which is contravariant in the ``send()`` argument
|
||||
type (see below).
|
||||
``MutableSequence``) are declared invariant. The one example of
|
||||
a contravariant type is the ``Generator`` type, which is contravariant
|
||||
in the ``send()`` argument type (see below).
|
||||
|
||||
Note: variance affects type parameters for generic types -- it does
|
||||
not affect regular parameters. For example, the following example is
|
||||
Note: Covariance or contravariance is *not* a property of a type variable,
|
||||
but a property of a generic class defined using this variable.
|
||||
Variance is only applicable to generic types; generic functions
|
||||
do not have this property. The latter should be defined using only
|
||||
type variables without ``covariant`` or ``contravariant`` keyword arguments.
|
||||
For example, the following example is
|
||||
fine::
|
||||
|
||||
from typing import TypeVar
|
||||
|
@ -780,12 +786,19 @@ fine::
|
|||
|
||||
class Manager(Employee): ...
|
||||
|
||||
E = TypeVar('E', bound=Employee) # Invariant
|
||||
E = TypeVar('E', bound=Employee)
|
||||
|
||||
def dump_employee(e: E) -> None: ...
|
||||
|
||||
dump_employee(Manager()) # OK
|
||||
|
||||
while the following is prohibited::
|
||||
|
||||
B_co = TypeVar('B_co', covariant=True)
|
||||
|
||||
def bad_func(x: B_co) -> B_co: # Flagged as error by a type checker
|
||||
...
|
||||
|
||||
|
||||
The numeric tower
|
||||
-----------------
|
||||
|
@ -1728,10 +1741,10 @@ Generic variants of container ABCs (and a few non-containers):
|
|||
* Generator, used as ``Generator[yield_type, send_type,
|
||||
return_type]``. This represents the return value of generator
|
||||
functions. It is a subtype of ``Iterable`` and it has additional
|
||||
type variables for the type accepted by the ``send()`` method (which
|
||||
is contravariant -- a generator that accepts sending it ``Employee``
|
||||
instance is valid in a context where a generator is required that
|
||||
accepts sending it ``Manager`` instances) and the return type of the
|
||||
type variables for the type accepted by the ``send()`` method (it
|
||||
is contravariant in this variable -- a generator that accepts sending it
|
||||
``Employee`` instance is valid in a context where a generator is required
|
||||
that accepts sending it ``Manager`` instances) and the return type of the
|
||||
generator.
|
||||
|
||||
* Hashable (not generic, but present for completeness)
|
||||
|
|
Loading…
Reference in New Issue