type infer check python python-3.x type-hinting

infer - python typing



¿Qué son las sugerencias de tipo en Python 3.5? (3)

Se dice que una de las características mencionadas en Python 3.5 son las type hints .

En este article se menciona un ejemplo de type hints de type hints y también se menciona el uso responsable de sugerencias de tipo. ¿Alguien puede explicar más al respecto y cuándo debe usarse y cuándo no?


Agregando a la elaborada respuesta de Jim:

Compruebe el typing : este módulo admite sugerencias de tipo según lo especificado por PEP 484 .

Por ejemplo, la siguiente función toma y devuelve valores de tipo str y se anota de la siguiente manera:

def greeting(name: str) -> str: return ''Hello '' + name

El módulo de typing también admite:

  1. Alias ​​de tipo .
  2. Escriba sugerencias para funciones de devolución de llamada .
  3. Generics : las clases base abstractas se han ampliado para admitir la suscripción para denotar los tipos esperados de elementos de contenedor.
  4. Tipos genéricos definidos por el usuario: una clase definida por el usuario se puede definir como una clase genérica.
  5. Cualquier tipo : cada tipo es un subtipo de Cualquiera.

El recientemente lanzado PyCharm 5 admite sugerencias de tipo. En su publicación de blog al respecto (consulte Sugerencias de tipo PyCharm ) ofrecen una excelente explicación de qué sugerencias de tipo son y qué no, junto con varios ejemplos e ilustraciones sobre cómo usarlas en su código.

Además, es compatible con Python 2.7, como se explica en este comentario :

PyCharm admite el módulo de escritura de PyPI para Python 2.7, Python 3.2-3.4. Para 2.7, debe poner sugerencias de tipo en los archivos de apéndice * .pyi, ya que las anotaciones de funciones se agregaron en Python 3.0 .


Sugeriría leer PEP 483 y PEP 484 y ver this presentación de Guido sobre Sugerencias de tipo.

En pocas palabras : la sugerencia de tipo es literalmente lo que significan las palabras, usted insinúa el tipo de objeto (s) que está utilizando .

Debido a la naturaleza dinámica de Python, inferir o verificar el tipo de un objeto que se está utilizando es especialmente difícil. Este hecho dificulta que los desarrolladores comprendan qué está sucediendo exactamente en el código que no han escrito y, lo más importante, las herramientas de verificación de tipos que se encuentran en muchos IDE [PyCharm, PyDev vienen a mi mente] que están limitados debido al hecho de que no tienen ningún indicador de qué tipo son los objetos. Como resultado, recurren a tratar de inferir el tipo con (como se mencionó en la presentación) alrededor del 50% de tasa de éxito.

Para tomar dos diapositivas importantes de la presentación Sugerencia de tipo:

¿Por qué escribir pistas?

  1. Ayuda a los verificadores de tipos : al insinuar qué tipo de objeto quiere que sea el objeto, el verificador de tipos puede detectar fácilmente si, por ejemplo, está pasando un objeto con un tipo que no se espera.
  2. Ayuda con la documentación: una tercera persona que vea su código sabrá qué se espera dónde, por ejemplo, cómo usarlo sin obtener TypeErrors .
  3. Ayuda a los IDE a desarrollar herramientas más precisas y robustas: los entornos de desarrollo serán más adecuados para sugerir métodos apropiados cuando sepan qué tipo es su objeto. Probablemente haya experimentado esto con algún IDE en algún momento, golpeando el . y tener métodos / atributos emergentes que no están definidos para un objeto.

¿Por qué usar Checkers tipo estático?

  • Encuentra errores antes : creo que esto es evidente.
  • Cuanto más grande sea su proyecto, más lo necesitará : nuevamente, tiene sentido. Los lenguajes estáticos ofrecen una robustez y control de los que carecen los lenguajes dinámicos. Cuanto más grande y compleja sea su aplicación, más control y previsibilidad (desde un aspecto de comportamiento) necesitará.
  • Los grandes equipos ya están ejecutando análisis estáticos : supongo que esto verifica los dos primeros puntos.

Como nota final para esta pequeña introducción : esta es una característica opcional y, por lo que entiendo, se ha introducido para obtener algunos de los beneficios de la escritura estática.

Por lo general , no necesita preocuparse por ello y definitivamente no necesita usarlo (especialmente en los casos en que usa Python como un lenguaje de script auxiliar). Debería ser útil al desarrollar grandes proyectos ya que ofrece la robustez, el control y las capacidades de depuración adicionales que tanto se necesitan .

Escriba Sugerencia con mypy :

Para que esta respuesta sea más completa, creo que una pequeña demostración sería adecuada. mypy , la biblioteca que inspiró Type Hints tal como se presentan en el PEP. Esto está escrito principalmente para cualquiera que se encuentre con esta pregunta y se pregunte por dónde comenzar.

Antes de hacerlo, permítanme reiterar lo siguiente: PEP 484 no impone nada; simplemente establece una dirección para las anotaciones de funciones y propone pautas sobre cómo se puede / se debe realizar la verificación de tipo. Puede anotar sus funciones e insinuar tantas cosas como desee; sus scripts se seguirán ejecutando independientemente de la presencia de anotaciones porque Python no las usa.

De todos modos, como se señala en la PEP, los tipos de sugerencias generalmente deben tomar tres formas:

Además, querrá usar sugerencias de tipo junto con el nuevo módulo de typing introducido en Py3.5 . En él, se definen muchos ABC (clases base abstractas) (adicionales) junto con funciones auxiliares y decoradores para su uso en la comprobación estática. La mayoría de los ABCs en collections.abc se incluyen pero en forma Generic para permitir la suscripción (definiendo un método __getitem__() ).

Para cualquier persona interesada en una explicación más detallada de estos, la mypy documentation está escrita muy bien y tiene muchos ejemplos de código que demuestran / describen la funcionalidad de su verificador; definitivamente vale la pena leerlo.

Anotaciones de funciones y comentarios especiales:

Primero, es interesante observar algunos de los comportamientos que podemos obtener al usar comentarios especiales. # type: type especial # type: type se pueden agregar comentarios de # type: type durante las asignaciones de variables para indicar el tipo de un objeto si no se puede inferir directamente uno. Las tareas simples generalmente se infieren fácilmente, pero otras, como las listas (con respecto a su contenido), no pueden.

Nota: Si queremos usar cualquier derivado de Containers y necesitamos especificar el contenido de ese contenedor, debemos usar los tipos genéricos del módulo de typing . Estos soportan la indexación.

# generic List, supports indexing. from typing import List # In this case, the type is easily inferred as type: int. i = 0 # Even though the type can be inferred as of type list # there is no way to know the contents of this list. # By using type: List[str] we indicate we want to use a list of strings. a = [] # type: List[str] # Appending an int to our list # is statically not correct. a.append(i) # Appending a string is fine. a.append("i") print(a) # [0, ''i'']

Si agregamos estos comandos a un archivo y los ejecutamos con nuestro intérprete, todo funciona bien e print(a) solo imprime el contenido de la lista a . Los comentarios de # type han sido descartados, tratados como comentarios simples que no tienen un significado semántico adicional .

Al ejecutar esto con mypy , por otro lado, obtenemos la siguiente respuesta:

(Python3)jimmi@jim: mypy typeHintsCode.py typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"

Indicando que una lista de objetos str no puede contener un int , que, estáticamente hablando, es sonido. Esto se puede solucionar ya sea respetando el tipo de a y solo agregando objetos str o cambiando el tipo de contenido de a para indicar que cualquier valor es aceptable (Realizado de forma intuitiva con List[Any] después de que Any haya sido importado de la typing ) .

Las anotaciones de función se agregan en la forma param_name : type después de cada parámetro en la firma de su función y se especifica un tipo de retorno utilizando la notación de -> type antes de la función de finalización de dos puntos; todas las anotaciones se almacenan en el atributo __annotations__ para esa función en un práctico formulario de diccionario. Usando un ejemplo trivial (que no requiere tipos adicionales del módulo de typing ):

def annotated(x: int, y: str) -> bool: return x < y

El atributo annotated.__annotations__ ahora tiene los siguientes valores:

{''y'': <class ''str''>, ''return'': <class ''bool''>, ''x'': <class ''int''>}

Si somos un novato completo, o estamos familiarizados con los conceptos de Py2.7 y, en consecuencia, desconocemos el TypeError acecha en la comparación de annotated , podemos realizar otra comprobación estática, detectar el error y ahorrarnos algunos problemas:

(Python3)jimmi@jim: mypy typeHintsCode.py typeFunction.py: note: In function "annotated": typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")

Entre otras cosas, llamar a la función con argumentos no válidos también quedará atrapado:

annotated(20, 20) # mypy complains: typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"

Estos pueden extenderse básicamente a cualquier caso de uso y los errores detectados se extienden más allá de las llamadas y operaciones básicas. Los tipos que puede verificar son realmente flexibles y simplemente he dado un pequeño adelanto de su potencial. Una mirada en el módulo de typing , los PEP o los documentos mypy le dará una idea más completa de las capacidades ofrecidas.

Archivos Stub:

Los archivos de código auxiliar se pueden usar en dos casos diferentes no mutuamente excluyentes:

  • Debe escribir marque un módulo para el que no desea alterar directamente las firmas de funciones
  • Desea escribir módulos y tener verificación de tipo, pero además desea separar las anotaciones del contenido.

Los archivos de código auxiliar (con una extensión de .pyi ) son una interfaz anotada del módulo que está creando / desea utilizar. Contienen las firmas de las funciones que desea verificar con el cuerpo de las funciones descartadas. Para tener una idea de esto, dado un conjunto de tres funciones aleatorias en un módulo llamado randfunc.py :

def message(s): print(s) def alterContents(myIterable): return [i for i in myIterable if i % 2 == 0] def combine(messageFunc, itFunc): messageFunc("Printing the Iterable") a = alterContents(range(1, 20)) return set(a)

Podemos crear un archivo randfunc.pyi , en el que podemos colocar algunas restricciones si lo deseamos. La desventaja es que alguien que vea la fuente sin el código auxiliar realmente no recibirá esa ayuda de anotación cuando intente comprender qué se supone que se debe pasar a dónde.

De todos modos, la estructura de un archivo stub es bastante simplista: agregue todas las definiciones de funciones con cuerpos vacíos ( pass lleno) y proporcione las anotaciones basadas en sus requisitos. Aquí, supongamos que solo queremos trabajar con tipos int para nuestros contenedores.

# Stub for randfucn.py from typing import Iterable, List, Set, Callable def message(s: str) -> None: pass def alterContents(myIterable: Iterable[int])-> List[int]: pass def combine( messageFunc: Callable[[str], Any], itFunc: Callable[[Iterable[int]], List[int]] )-> Set[int]: pass

La función de combine da una indicación de por qué es posible que desee utilizar anotaciones en un archivo diferente, algunas veces abarrotan el código y reducen la legibilidad (gran no-no para Python). Por supuesto, podría usar alias de tipo, pero eso a veces confunde más de lo que ayuda (así que úselos sabiamente).

Esto debería familiarizarlo con los conceptos básicos de Type Hints en Python. A pesar de que el verificador de tipo utilizado ha sido mypy , debería comenzar gradualmente a ver más ventanas emergentes, algunas internamente en IDEs ( PyCharm ,) y otras como módulos estándar de Python. Intentaré agregar verificadores adicionales / paquetes relacionados en la siguiente lista cuando y si los encuentro (o si se sugiere).

Damas que conozco :

  • mypy : como se describe aquí.
  • PyType : por Google, utiliza una notación diferente de la que PyType , probablemente valga la pena echarle un vistazo.

Paquetes / proyectos relacionados :

  • typeshed: repositorio oficial de Python que alberga una variedad de archivos stub para la biblioteca estándar.

El proyecto de typeshed es en realidad uno de los mejores lugares en los que puede mirar para ver cómo se pueden usar las sugerencias de tipo en un proyecto propio. Tomemos como ejemplo los __init__ dunders de la clase Counter en el archivo .pyi correspondiente:

class Counter(Dict[_T, int], Generic[_T]): @overload def __init__(self) -> None: ... @overload def __init__(self, Mapping: Mapping[_T, int]) -> None: ... @overload def __init__(self, iterable: Iterable[_T]) -> None: ...

Donde _T = TypeVar(''_T'') se utiliza para definir clases genéricas . Para la clase Counter podemos ver que no puede tomar argumentos en su inicializador, obtener una sola Mapping de cualquier tipo a un int o tomar un Iterable de cualquier tipo.

Aviso : Una cosa que olvidé mencionar es que el módulo de typing se ha introducido de forma provisional . De PEP 411 :

Un paquete provisional puede tener su API modificada antes de "graduarse" en un estado "estable". Por un lado, este estado proporciona al paquete los beneficios de ser formalmente parte de la distribución de Python. Por otro lado, el equipo central de desarrollo declara explícitamente que no se hacen promesas con respecto a la estabilidad de la API del paquete, que puede cambiar para la próxima versión. Si bien se considera un resultado improbable, dichos paquetes pueden incluso eliminarse de la biblioteca estándar sin un período de desaprobación si las preocupaciones con respecto a su API o mantenimiento demuestran ser fundadas.

Así que toma las cosas aquí con una pizca de sal; Dudo que se elimine o altere de manera significativa, pero uno nunca puede saberlo.

** Otro tema completo pero válido en el ámbito de las sugerencias de tipo: PEP 526 : sintaxis para anotaciones de variables es un esfuerzo para reemplazar los comentarios de # type mediante la introducción de una nueva sintaxis que permite a los usuarios anotar el tipo de variables en simples declaraciones de varname: type .

Consulte ¿Qué son las anotaciones variables en Python 3.6? , como se mencionó anteriormente, para una pequeña introducción sobre estos.