¿Qué son las anotaciones variables en Python 3.6?

Python 3.6 está a punto de ser lanzado. PEP 494 – La progtwigción de lanzamientos de Python 3.6 menciona el fin de diciembre, así que revisé las novedades de Python 3.6 para ver que mencionan las anotaciones variables :

PEP 484 introdujo el estándar para anotaciones de tipo de parámetros de función, también conocido como sugerencias de tipo. Este PEP agrega syntax a Python para anotar los tipos de variables, incluidas las variables de clase y las variables de instancia:

primes: List[int] = [] captain: str # Note: no initial value! class Starship: stats: Dict[str, int] = {} 

Al igual que para las anotaciones de funciones, el intérprete de Python no asocia ningún significado particular a las anotaciones de variables y solo las almacena en un atributo especial __annotations__ de una clase o módulo. A diferencia de las declaraciones de variables en lenguajes de tipo estático, el objective de la syntax de anotación es proporcionar una manera fácil de especificar metadatos de tipo estructurado para bibliotecas y herramientas de terceros a través del árbol de syntax abstracta y el atributo __annotations__ .

Entonces, de lo que leí, son parte de las sugerencias de tipo que vienen de Python 3.5, que se describen en Qué son las sugerencias de tipo en Python 3.5 .

Sigo el ejemplo de captain: str y class Starship , pero no estoy seguro acerca del último: ¿Cómo se explica los primes: List[int] = [] ? ¿Está definiendo una lista vacía que solo permitirá enteros?

    Todo entre : y = es una sugerencia de tipo, por lo que los primes se definen como List[int] , y se establecen inicialmente en una lista vacía (y las stats son inicialmente un diccionario vacío, definido como Dict[str, int] ).

    List[int] y Dict[str, int] no son parte de la siguiente syntax, sin embargo, estos ya estaban definidos en el PEP de sugerencias de tipeo de Python 3.5. La propuesta 3.6 PEP 526 – Sintaxis para anotaciones variables solo define la syntax para adjuntar las mismas sugerencias a las variables; antes solo podía adjuntar sugerencias de tipo a las variables con comentarios (por ejemplo, primes = [] # List[int] ).

    Tanto la List como el Dict son tipos generics , lo que indica que tiene una lista o una asignación de diccionario con contenidos específicos (concretos).

    Para la List , solo hay un ‘argumento’ (los elementos en la [...] syntax), el tipo de cada elemento en la lista. Para Dict , el primer argumento es el tipo de clave y el segundo el tipo de valor. Entonces, todos los valores en la lista de primes son enteros, y todos los pares clave-valor en el diccionario de stats son pares (str, int) , asignando cadenas a enteros.

    Consulte las definiciones de typing.List and typing.Dict , la sección sobre generics , así como PEP 483 – La teoría de las sugerencias de tipo .

    Al igual que las sugerencias de tipo en las funciones, su uso es opcional y también se consideran anotaciones (siempre que haya un objeto para adjuntarlas, así como elementos globales en los módulos y atributos en las clases, pero no locales en las funciones) que puede __annotations__ mediante el atributo __annotations__ . Puede adjuntar información arbitraria a estas anotaciones, no está estrictamente limitado a la información de sugerencia de tipo.

    Es posible que desee leer la propuesta completa ; contiene alguna funcionalidad adicional más allá de la nueva syntax; especifica cuándo se evalúan dichas anotaciones, cómo inspeccionarlas y cómo declarar algo como un atributo de clase frente a un atributo de instancia, por ejemplo.

    ¿Qué son las anotaciones variables?

    Las anotaciones variables son solo el siguiente paso de los comentarios de # type , como se definieron en PEP 484 ; la razón detrás de este cambio se destaca en la sección respectiva de PEP 526 .

    Entonces, en lugar de insinuar el tipo con:

     primes = [] # type: List[int] 

    Se introdujo una nueva syntax para permitir anotar directamente el tipo con una asignación del formulario:

     primes: List[int] = [] 

    que, como lo señaló @Martijn, denota una lista de enteros usando tipos disponibles al typing e inicializarla en una lista vacía.

    ¿Qué cambios trae?

    El primer cambio introducido fue una nueva syntax que le permite anotar un nombre con un tipo, ya sea independiente después del carácter : o opcionalmente anotar, mientras que también le asigna un valor:

     annotated_assignment_stmt ::= augtarget ":" expression ["=" expression] 

    Así que el ejemplo en cuestión:

      primes: List[int] = [ ] # ^ ^ ^ # augtarget | | # expression | # expression (optionally initialize to empty list) 

    También se introdujeron cambios adicionales junto con la nueva syntax; los módulos y las clases ahora tienen un atributo __annotations__ (como las funciones han tenido desde PEP 3107 – Anotaciones de funciones ) en las que se adjunta el tipo de metadatos:

     from typing import get_type_hints # grabs __annotations__ 

    Ahora __main__.__annotations__ contiene los tipos declarados:

     >>> from typing import List, get_type_hints >>> primes: List[int] = [] >>> captain: str >>> import __main__ >>> get_type_hints(__main__) {'primes': typing.List<~T>[int]} 

    captain no se mostrará actualmente a través de get_type_hints porque get_type_hints solo devuelve tipos a los que también se puede acceder en un módulo; Es decir, primero necesita un valor:

     >>> captain = "Picard" >>> get_type_hints(__main__) {'primes': typing.List<~T>[int], 'captain': } 

    El uso de print(__annotations__) mostrará 'captain': pero realmente no debería estar accediendo a __annotations__ directamente.

    Del mismo modo, para las clases:

     >>> get_type_hints(Starship) ChainMap({'stats': typing.Dict<~KT, ~VT>[str, int]}, {}) 

    Donde se utiliza un ChainMap para capturar las anotaciones de una clase determinada (ubicadas en la primera asignación) y todas las anotaciones definidas en las clases básicas encontradas en su mro (asignaciones consecuentes, {} para el objeto).

    Junto con la nueva syntax, se ha agregado un nuevo tipo de ClassVar para indicar las variables de clase. Sí, las stats en su ejemplo son en realidad una variable de instancia , no una ClassVar .

    ¿Seré obligado a usarlo?

    Al igual que con las sugerencias de tipo de PEP 484 , estas son completamente opcionales y son de uso principal para las herramientas de verificación de tipo (y cualquier otra cosa que pueda construir en base a esta información). Debe ser provisional cuando se lance la versión estable de Python 3.6 para que se puedan agregar pequeños ajustes en el futuro.