From 510b267a09dfccccd6ede6f965dc323620a0e837 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Fri, 7 Apr 2017 04:06:19 -0700 Subject: [PATCH 1/6] Clarify generic class attribute access See https://github.com/python/mypy/issues/2878 --- pep-0484.txt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pep-0484.txt b/pep-0484.txt index b15fe3b05db..20d02fafb93 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -583,6 +583,7 @@ argument(s) is substituted. Otherwise, ``Any`` is assumed. Example:: class Node(Generic[T]): def __init__(self, label: T = None) -> None: ... + x: None # Type: T x = Node('') # Inferred type is Node[str] y = Node(0) # Inferred type is Node[int] @@ -610,6 +611,21 @@ the runtime class of the objects created by instantiating them doesn't record the distinction. This behavior is called "type erasure"; it is common practice in languages with generics (e.g. Java, TypeScript). +Class attribute of a generic class can be looked up only through the instance +of the class; any attempt to use generic classes (parametrized or not) to +access attributes will result in both type check failure and runtime error:: + + # (continued from previous example) + Node[int].x = 1 # Error + Node[int].x # Error + Node.x = 1 # Error + Node.x # Error + type(p).x # Error + p.x # Ok + Node[int]().x # Ok + +In addition, ``ClassVar`` cannot be parametrized. + Generic versions of abstract collections like ``Mapping`` or ``Sequence`` and generic versions of built-in classes -- ``List``, ``Dict``, ``Set``, and ``FrozenSet`` -- cannot be instantiated. However, concrete user-defined @@ -2439,7 +2455,7 @@ Copyright This document has been placed in the public domain. - + .. Local Variables: mode: indented-text From 2bcdba4c79c3c8202c60397eee23dec71f654147 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Fri, 7 Apr 2017 11:57:28 -0700 Subject: [PATCH 2/6] Address CR --- pep-0484.txt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 20d02fafb93..44cbd648dfc 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -288,7 +288,7 @@ elements. Example:: def notify_by_email(employees: Set[Employee], overrides: Mapping[str, str]) -> None: ... -Generics can be parametrized by using a new factory available in +Generics can be parameterized by using a new factory available in ``typing`` called ``TypeVar``. Example:: from typing import Sequence, TypeVar @@ -583,7 +583,7 @@ argument(s) is substituted. Otherwise, ``Any`` is assumed. Example:: class Node(Generic[T]): def __init__(self, label: T = None) -> None: ... - x: None # Type: T + x = None # Type: T x = Node('') # Inferred type is Node[str] y = Node(0) # Inferred type is Node[int] @@ -611,9 +611,11 @@ the runtime class of the objects created by instantiating them doesn't record the distinction. This behavior is called "type erasure"; it is common practice in languages with generics (e.g. Java, TypeScript). -Class attribute of a generic class can be looked up only through the instance -of the class; any attempt to use generic classes (parametrized or not) to -access attributes will result in both type check failure and runtime error:: +Using generic classes (parameterized or not) to access attributes will result +in both type check failure and runtime error. Outside the class definition +body, a class attribute cannot be assigned, and can only be looked up by +accessing it through the class instance that does not have same-named +instance attribute:: # (continued from previous example) Node[int].x = 1 # Error @@ -621,10 +623,11 @@ access attributes will result in both type check failure and runtime error:: Node.x = 1 # Error Node.x # Error type(p).x # Error - p.x # Ok - Node[int]().x # Ok + p.x # Ok (evaluates to None) + Node[int]().x # Ok (evaluates to None) + p.x = 1 # Will assign to instance attribute -In addition, ``ClassVar`` cannot be parametrized. +In addition, ``ClassVar`` cannot be parameterized. Generic versions of abstract collections like ``Mapping`` or ``Sequence`` and generic versions of built-in classes -- ``List``, ``Dict``, ``Set``, From dbe08a38733bd7dd40b2d546f002a305635b09e9 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Fri, 7 Apr 2017 22:00:58 -0700 Subject: [PATCH 3/6] Signed CLA --- pep-0484.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0484.txt b/pep-0484.txt index 44cbd648dfc..9a65e8b3a32 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -625,7 +625,7 @@ instance attribute:: type(p).x # Error p.x # Ok (evaluates to None) Node[int]().x # Ok (evaluates to None) - p.x = 1 # Will assign to instance attribute + p.x = 1 # Ok, but assigning to instance attribute In addition, ``ClassVar`` cannot be parameterized. From 8bfc79a769c210e1d9fa18dfd66c36a5f023ae62 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Mon, 10 Apr 2017 17:36:37 -0700 Subject: [PATCH 4/6] Clarify that type variables cannot be used inside TypeVar --- pep-0484.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 9a65e8b3a32..d5d8c68cf1a 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -306,10 +306,11 @@ variable (it should not be used as part of a larger expression). The argument to ``TypeVar()`` must be a string equal to the variable name to which it is assigned. Type variables must not be redefined. -``TypeVar`` supports constraining parametric types to a fixed set of -possible types. For example, we can define a type variable that ranges -over just ``str`` and ``bytes``. By default, a type variable ranges -over all possible types. Example of constraining a type variable:: +``TypeVar`` supports constraining parametric types to a fixed set of possible +types (note: those types cannot be parametrized by type variables). For +example, we can define a type variable that ranges over just ``str`` and +``bytes``. By default, a type variable ranges over all possible types. +Example of constraining a type variable:: from typing import TypeVar @@ -720,11 +721,11 @@ classes without a metaclass conflict. Type variables with an upper bound ---------------------------------- -A type variable may specify an upper bound using ``bound=``. -This means that an actual type substituted (explicitly or implicitly) -for the type variable must be a subtype of the boundary type. A -common example is the definition of a Comparable type that works well -enough to catch the most common errors:: +A type variable may specify an upper bound using ``bound=`` (note: + itself cannot be parametrized by type variables). This means that an +actual type substituted (explicitly or implicitly) for the type variable must +be a subtype of the boundary type. A common example is the definition of a +Comparable type that works well enough to catch the most common errors:: from typing import TypeVar From dca0a3657ced19ba43cf73486515e9265d53d6bc Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Tue, 11 Apr 2017 12:06:55 -0700 Subject: [PATCH 5/6] Clarify the usage of type variable inside Type --- pep-0484.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index d5d8c68cf1a..54eec7925da 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -1206,9 +1206,13 @@ 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. +``Type[Any]`` is also supported (see below for its meaning). + +``Type[T]`` where ``T`` is a type variable is allowed when annotating the +first argument of a class method (see the relevant section). + +Any other special constructs like ``Tuple`` or ``Callable`` are not allowed +as an argument to ``Type``. There are some concerns with this feature: for example when ``new_user()`` calls ``user_class()`` this implies that all subclasses From 1d660c37ab5b0c40d43602166b74d1570ffac576 Mon Sep 17 00:00:00 2001 From: Max Moroz Date: Wed, 10 May 2017 16:20:50 -0700 Subject: [PATCH 6/6] CR fixes --- pep-0484.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 54eec7925da..7bea81d254c 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -613,10 +613,9 @@ record the distinction. This behavior is called "type erasure"; it is common practice in languages with generics (e.g. Java, TypeScript). Using generic classes (parameterized or not) to access attributes will result -in both type check failure and runtime error. Outside the class definition -body, a class attribute cannot be assigned, and can only be looked up by -accessing it through the class instance that does not have same-named -instance attribute:: +in type check failure. Outside the class definition body, a class attribute +cannot be assigned, and can only be looked up by accessing it through the +class instance that does not have same-named instance attribute:: # (continued from previous example) Node[int].x = 1 # Error @@ -628,8 +627,6 @@ instance attribute:: Node[int]().x # Ok (evaluates to None) p.x = 1 # Ok, but assigning to instance attribute -In addition, ``ClassVar`` cannot be parameterized. - Generic versions of abstract collections like ``Mapping`` or ``Sequence`` and generic versions of built-in classes -- ``List``, ``Dict``, ``Set``, and ``FrozenSet`` -- cannot be instantiated. However, concrete user-defined @@ -725,7 +722,7 @@ A type variable may specify an upper bound using ``bound=`` (note: itself cannot be parametrized by type variables). This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subtype of the boundary type. A common example is the definition of a -Comparable type that works well enough to catch the most common errors:: +``Comparable`` type that works well enough to catch the most common errors:: from typing import TypeVar