From 55156447eade55600cf6097531131c0df71b7388 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 3 Sep 2016 20:56:44 +0200 Subject: [PATCH 1/3] Allow annotating first argument of instance and class methods --- pep-0484.txt | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/pep-0484.txt b/pep-0484.txt index 437703c2c09..d2c1d6a2379 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -109,7 +109,7 @@ It is recommended but not required that checked functions have annotations for all arguments and the return type. For a checked function, the default annotation for arguments and for the return type is ``Any``. An exception is that the first argument of instance and -class methods does not need to be annotated; it is assumed to have the +class methods. If it is not annotated, then it is assumed to have the type of the containing class for instance methods, and a type object type corresponding to the containing class object for class methods. For example, in class ``A`` the first argument of an instance method @@ -1120,6 +1120,56 @@ subtype of ``Type[Base]``:: ... +Annotating instance and class methods +------------------------------------- + +In most cases the first argument of class and instance methods +does not need to be annotated, and it is assumed to have the +type of the containing class for instance methods, and a type object +type corresponding to the containing class object for class methods. +In addition, the first argument in an instance method can be annotated +with a type variable. In this case the return type uses the same +type variable, thus making that method a generic function. For example:: + + T = TypeVar('T', bound='Copyable') + class Copyable: + def copy(self: T) -> T: + # return a copy of self + + class C(Copyable): ... + c = C() + c2 = c.copy() # type here should be C + +The same applies to class methods using ``Type[]`` in an annotation +of the first argument:: + + T = TypeVar('T', bound='C') + class C: + def factory(cls: Type[T]) -> T: + # make a new instance of cls + + class D(C): ... + d = D.factory() # type here should be D + +Note that some type checkers may apply restrictions on this use, such as +requiring an appropriate upper bound for the type variable used +(see examples). Also this use case should not be confused with the use +of type variables in methods of generic classes. Compare:: + + T = TypeVar('T') + class Num(Generic[T]): + def __init__(self, val: T) -> None: ... + def __add__(self, other: 'Num[T]') -> 'Num[T]': ... + Num(1) + Num(3.14) # Rejected by type checker + + N = TypeVar('N', bound='Float') + class Float: + def __init__(self, val) -> None: ... + def __add__(self: N, other: N) -> N: ... + class Int(Float): ... + Int(1) + Float(3.14) # OK, the inferred value of T is Float + + Version and platform checking ----------------------------- From b466fa4539cd61c949d7d28ba56dd4cbe984599c Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Sep 2016 00:36:02 +0200 Subject: [PATCH 2/3] Response to comments --- pep-0484.txt | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index d2c1d6a2379..a1eaafbcbc7 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -108,7 +108,7 @@ comment should be treated as having no annotations. It is recommended but not required that checked functions have annotations for all arguments and the return type. For a checked function, the default annotation for arguments and for the return type -is ``Any``. An exception is that the first argument of instance and +is ``Any``. An exception is the first argument of instance and class methods. If it is not annotated, then it is assumed to have the type of the containing class for instance methods, and a type object type corresponding to the containing class object for class methods. @@ -1128,7 +1128,7 @@ does not need to be annotated, and it is assumed to have the type of the containing class for instance methods, and a type object type corresponding to the containing class object for class methods. In addition, the first argument in an instance method can be annotated -with a type variable. In this case the return type uses the same +with a type variable. In this case the return type may use the same type variable, thus making that method a generic function. For example:: T = TypeVar('T', bound='Copyable') @@ -1153,21 +1153,7 @@ of the first argument:: Note that some type checkers may apply restrictions on this use, such as requiring an appropriate upper bound for the type variable used -(see examples). Also this use case should not be confused with the use -of type variables in methods of generic classes. Compare:: - - T = TypeVar('T') - class Num(Generic[T]): - def __init__(self, val: T) -> None: ... - def __add__(self, other: 'Num[T]') -> 'Num[T]': ... - Num(1) + Num(3.14) # Rejected by type checker - - N = TypeVar('N', bound='Float') - class Float: - def __init__(self, val) -> None: ... - def __add__(self: N, other: N) -> N: ... - class Int(Float): ... - Int(1) + Float(3.14) # OK, the inferred value of T is Float +(see examples). Version and platform checking From c5e553d3b74f5a40d65761ed0a1f804012f47910 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 4 Sep 2016 11:56:16 +0200 Subject: [PATCH 3/3] Added missing @classmethod to example --- pep-0484.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/pep-0484.txt b/pep-0484.txt index a1eaafbcbc7..4b46b083c24 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -1145,6 +1145,7 @@ of the first argument:: T = TypeVar('T', bound='C') class C: + @classmethod def factory(cls: Type[T]) -> T: # make a new instance of cls