diff --git a/pep-0484.txt b/pep-0484.txt index 1858e4cc4..491e31024 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -448,6 +448,92 @@ is not generic but implicitly inherits from ``Iterable[Any]``:: Generic metaclasses are not supported. +Scoping rules for type variables +-------------------------------- + +Type variables follow normal name resolution rules. +However, there are some special cases in the static typechecking context: + +* A type variable used in a generic function could be inferred to be equal to + different types in the same code block. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + def fun_1(x: T) -> T: ... # T here + def fun_2(x: T) -> T: ... # and here could be different + + fun_1(1) # This is OK, T is inferred to be int + fun_2('a') # This is aslo OK, now T is str + +* A type variable used in a method of a generic class that coincisides + with one of the variables that parameterize this class is always bound + to that variable. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class MyClass(Generic[T]): + def meth_1(self, x: T) -> T: ... # T here + def meth_2(self, x: T) -> T: ... # and here are always the same + + a = MyClass() # type: MyClass[int] + a.meth_1(1) # OK + a.meth_2('a') # This is an error! + +* A type variable used in a method that does not match any of the variables + that parameterize the class makes this method a generic function in that + variable:: + + T = TypeVar('T') + S = TypeVar('S') + class Foo(Generic[T]): + def method(self, x: T, y: S) -> S: + ... + + x = Foo() # type: Foo[int] + y = x.method(0, "abc") # inferred type of y is str + +* Unbound type variables should not appear in the bodies of generic functions, + or in the class bodies apart from method definitions:: + + T = TypeVar('T') + S = TypeVar('S') + + def a_fun(x: T) -> None: + # this is OK + y = [] # type: List[T] + # but below is an error! + y = [] # type: List[S] + + class Bar(Generic[T]): + # this is also an error + an_attr = [] # type: List[S] + + def do_something(x: S) -> S: # this is OK though + ... + +* A generic class definition that appears inside a generic function + should not use type variables that parameterize the generic function:: + + from typing import List + + def a_fun(x: T) -> None: + + # This is OK + a_list = [] # type: List[T] + ... + + # This is however illegal + class MyGeneric(Generic[T]): + ... + +* A generic class nested in another generic class cannot use the same + type variables, unless the inner class definition is inside a function. + + Instantiating generic classes and type erasure ---------------------------------------------- @@ -1297,11 +1383,11 @@ from ``UserId`` where ``int`` is expected. Examples:: num = UserId(5) + 1 # type: int -``NewType`` accepts only one argument that shoud be a proper class, -i.e., not a type construct like ``Union``, etc. The function returned -by ``NewType`` accepts only one argument; this is equivalent to supporting -only one constructor accepting an instance of the base class (see above). -Example:: +``NewType`` accepts exactly two arguments: a name for the new unique type, +and a base class. The latter should be a proper class, i.e., +not a type construct like ``Union``, etc. The function returned by ``NewType`` +accepts only one argument; this is equivalent to supporting only one +constructor accepting an instance of the base class (see above). Example:: class PacketId: def __init__(self, major: int, minor: int) -> None: