Sugeriría leer PEP 483 y PEP 484 y ver esta 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 que es 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 sugerencias?
- Ayuda a los verificadores de tipos : al insinuar qué tipo de objeto desea 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 obtenerlo
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, presionando
.
y teniendo métodos / atributos emergentes que no están definidos para un objeto.
¿Por qué usar Checkers de tipo estático?
- Encuentra errores antes : creo que esto es evidente.
- Cuanto más grande sea su proyecto, más lo necesitará : una vez más, 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 eso 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. Usaré mypy
la biblioteca que inspiró las Sugerencias de tipo 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 en sí 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.
# type: type
Comentarios especiales que complementan 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
comentarios)
Además, querrá usar sugerencias de tipo junto con el nuevo typing
módulo 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 ABCs
en collections.abc
están incluidas, pero en una Generic
forma a fin de permitir la suscripción (mediante la definición de un __getitem__()
método).
Para cualquier persona interesada en una explicación más detallada de estos, el mypy documentation
está escrito 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. Se # type: type
pueden agregar comentarios especiales 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 derivada de Containers
y necesitamos especificar los contenidos para ese contenedor, debemos usar los tipos genéricos del typing
módulo. 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 y print(a)
solo imprime el contenido de la lista a
. Los # type
comentarios 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 str
objetos no puede contener un int
, que, estáticamente hablando, es sólido. Esto se puede solucionar ya sea respetando el tipo de objetos a
y solo agregándolos 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
se ha importado typing
).
Las anotaciones de función se agregan en el formulario param_name : type
después de cada parámetro en su firma de función y se especifica un tipo de retorno utilizando la -> type
notación antes de la función de finalización de dos puntos; todas las anotaciones se almacenan en el __annotations__
atributo para esa función en un práctico formulario de diccionario. Usando un ejemplo trivial (que no requiere tipos adicionales del typing
módulo):
def annotated(x: int, y: str) -> bool:
return x < y
El annotated.__annotations__
atributo ahora tiene los siguientes valores:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Si somos un novato completo, o estamos familiarizados con los Py2.7
conceptos y, en consecuencia, no somos conscientes del TypeError
acecho 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 typing
módulo, los PEP o los mypy
documentos 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 una 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 apéndice 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 entender qué se supone que se debe pasar a dónde.
De todos modos, la estructura de un archivo apéndice es bastante simplista: agregue todas las definiciones de funciones con cuerpos vacíos ( pass
rellenos) y proporcione las anotaciones basadas en sus requisitos. Aquí, supongamos que solo queremos trabajar con int
tipos 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 combine
función 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
gradual, debería comenzar 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 obtengo , probablemente valga la pena echarle un vistazo.
Paquetes / proyectos relacionados :
- typehed: repositorio oficial de Python que alberga una variedad de archivos stub para la biblioteca estándar.
El typeshed
proyecto 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__
usuarios de la Counter
clase en el .pyi
archivo 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 usa para definir clases genéricas . Para la Counter
clase podemos ver que no puede tomar argumentos en su inicializador, obtener un solo Mapping
de cualquier tipo a uno int
o tomar uno Iterable
de cualquier tipo.
Aviso : Una cosa que olvidé mencionar es que el typing
módulo 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 parte formal 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 nunca se puede saber.
** Otro tema completo pero válido en el ámbito de las sugerencias de tipo PEP 526
: La sintaxis para anotaciones de variables es un esfuerzo para reemplazar los # type
comentarios mediante la introducción de una nueva sintaxis que permite a los usuarios anotar el tipo de variables en varname: type
declaraciones simples .
Consulte ¿Qué son las anotaciones variables en Python 3.6? , como se mencionó anteriormente, para una pequeña introducción sobre estos.