Esta notebook fue creada originalmente como un blog post por Raúl E. López Briega en Mi blog sobre Python. El contenido esta bajo la licencia BSD.
Una de las razones por la que solemos amar a Python, es por su sistema de tipado dinámico, el cual lo convierte en un lenguaje de programación sumamente flexible y fácil de aprender; al no tener que preocuparnos por definir los tipos de los objetos, ya que Python los infiere por nosotros, podemos escribir programas en una forma mucho más productiva, sin verbosidad y utilizando menos líneas de código.
Ahora bien, este sistema de tipado dinámico también puede convertirse en una pesadilla en proyectos de gran escala, requiriendo varias horas de pruebas unitarias para evitar que los objetos adquieran un tipo de datos que no deberían y complicando el su mantenimiento o futura refactorización.
Por ejemplo, en un código tan trivial como el siguiente:
In [1]:
def saludo(nombre):
return 'Hola {}'.format(nombre)
Esta simple función nos va a devolver el texto 'Hola' seguido del nombre que le ingresemos; pero como no contiene ningún control sobre el tipo de datos que pude admitir la variable nombre, los siguientes casos serían igualmente válidos:
In [2]:
print (saludo('Raul'))
print (saludo(1))
En cambio, si pusiéramos un control sobre el tipo de datos que admitiera la variable nombre, para que siempre fuera un string, entonces el segundo caso ya no sería válido y lo podríamos detectar fácilmente antes de que nuestro programa se llegara a ejecutar.
Obviamente, para poder detectar el segundo error y que nuestra función saludo solo admita una variable del tipo string como argumento, podríamos reescribir nuestra función, agregando un control del tipo de datos de la siguiente manera:
In [3]:
def saludo(nombre):
if type(nombre) != str:
return "Error: el argumento debe ser del tipo String(str)"
return 'Hola {}'.format(nombre)
print(saludo('Raul'))
print(saludo(1))
Pero una solución más sencilla a tener que ir escribiendo condiciones para controlar los tipos de las variables o de las funciones es utilizar MyPy
MyPy es un proyecto que busca combinar los beneficios de un sistema de tipado dinámico con los de uno de tipado estático. Su meta es tener el poder y la expresividad de Python combinada con los beneficios que otorga el chequeo de los tipos de datos al momento de la compilación.
Algunos de los beneficios que proporciona utilizar MyPy son:
Estos son algunos de los tipos de datos más comunes que podemos encontrar en Python:
int
: Número entero de tamaño arbitrariofloat
: Número flotante.bool
: Valor booleano (True o False)str
: Unicode stringbytes
: 8-bit stringobject
: Clase base del que derivan todos los objecto en Python.List[str]
: lista de objetos del tipo string.Dict[str, int]
: Diccionario de string hacia enterosIterable[int]
: Objeto iterable que contiene solo enteros.Sequence[bool]
: Secuencia de valores booleanosAny
: Admite cualquier valor. (tipado dinámico)El tipo Any y los constructores List, Dict, Iterable y Sequence están definidos en el modulo typing que viene junto con MyPy.
Por ejemplo, volviendo al ejemplo del comienzo, podríamos reescribir la función saludo utilizando MyPy de forma tal que los tipos de datos sean explícitos y puedan ser chequeados al momento de la compilación.
In [4]:
%%writefile typeTest.py
import typing
def saludo(nombre: str) -> str:
return 'Hola {}'.format(nombre)
print(saludo('Raul'))
print(saludo(1))
En este ejemplo estoy creando un pequeño script y guardando en un archivo con el nombre 'typeTest.py', en la primer línea del script estoy importando la librería typing que viene con MyPy y es la que nos agrega la funcionalidad del chequeo de los tipos de datos. Luego simplemente ejecutamos este script utilizando el interprete de MyPy y podemos ver que nos va a detectar el error de tipo de datos en la segunda llamada a la función saludo.
In [5]:
!mypy typeTest.py
In [6]:
!python3 typeTest.py
En el ejemplo anterior, vimos como es la sintaxis para asignarle un tipo de datos a una función, la cual utiliza la sintaxis de Python3, annotations.
Si quisiéramos asignarle un tipo a una variable, podríamos utilizar la función Undefined
que viene junto con MyPy.
In [7]:
%%writefile typeTest.py
from typing import Undefined, List, Dict
# Declaro los tipos de las variables
texto = Undefined(str)
entero = Undefined(int)
lista_enteros = List[int]()
dic_str_int = Dict[str, int]()
# Asigno valores a las variables.
texto = 'Raul'
entero = 13
lista_enteros = [1, 2, 3, 4]
dic_str_int = {'raul': 1, 'ezequiel': 2}
# Intento asignar valores de otro tipo.
texto = 1
entero = 'raul'
lista_enteros = ['raul', 1, '2']
dic_str_int = {1: 'raul'}
In [8]:
!mypy typeTest.py
Otra alternativa que nos ofrece MyPy para asignar un tipo de datos a las variables, es utilizar comentarios; así, el ejemplo anterior lo podríamos reescribir de la siguiente forma, obteniendo el mismo resultado:
In [9]:
%%writefile typeTest.py
from typing import List, Dict
# Declaro los tipos de las variables
texto = '' # type: str
entero = 0 # type: int
lista_enteros = [] # type: List[int]
dic_str_int = {} # type: Dict[str, int]
# Asigno valores a las variables.
texto = 'Raul'
entero = 13
lista_enteros = [1, 2, 3, 4]
dic_str_int = {'raul': 1, 'ezequiel': 2}
# Intento asignar valores de otro tipo.
texto = 1
entero = 'raul'
lista_enteros = ['raul', 1, '2']
dic_str_int = {1: 'raul'}
In [10]:
!mypy typeTest.py
Instalar MyPy es bastante fácil, simplemente debemos seguir los siguientes pasos:
Si utilizan git, pueden clonar el repositorio de mypy:
$ git clone https://github.com/JukkaL/mypy.git
Si no utilizan git, como alternativa, se pueden descargar la última versión de mypy en el siguiente link:
https://github.com/JukkaL/mypy/archive/master.zip
Una vez que ya se lo descargaron, se posicionan dentro de la carpeta de mypy y ejecutan el script setup.py
para instalarlo:
$ python3 setup.py install
Reemplacen 'python3' con su interprete para python3.
Guido van Rossum, el creador de Python, ha enviado reciente una propuesta a la lista de correo de python-ideas, en la cual sugiere agregar en la próxima versión de Python la sintaxis de MyPy para las functions annotations. Pueden encontrar la propuesta en el siguiente link:
https://mail.python.org/pipermail/python-ideas/2014-August/028618.html
También pueden seguir las discusiones que se generaron sobre este tema en Reddit