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:
- Alias de tipo .
- Escriba sugerencias para funciones de devolución de llamada .
- Generics : las clases base abstractas se han ampliado para admitir la suscripción para denotar los tipos esperados de elementos de contenedor.
- Tipos genéricos definidos por el usuario: una clase definida por el usuario se puede definir como una clase genérica.
- 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?
- 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.
-
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
. -
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:
- Anotaciones de funciones. ( PEP 3107 )
- Archivos de código auxiliar para módulos integrados / de usuario.
-
Special
# type: type
comentarios que complementen las dos primeras formas. (Consulte: ¿Qué son las anotaciones variables en Python 3.6? Para una actualización de Python 3.6 para# type: type
comments)
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.