Tipos de parámetros de función en Python

A menos que me equivoque, crear una función en Python funciona así:

def my_func(param1, param2): # stuff 

Sin embargo, en realidad no se dan los tipos de esos parámetros. Además, si recuerdo, Python es un lenguaje fuertemente tipado, como tal, parece que Python no debería permitirle pasar un parámetro de un tipo diferente al que el creador de la función esperaba. Sin embargo, ¿cómo sabe Python que el usuario de la función está pasando los tipos adecuados? ¿El progtwig simplemente morirá si es del tipo incorrecto, asumiendo que la función realmente usa el parámetro? ¿Tienes que especificar el tipo?

Python está fuertemente tipado porque cada objeto tiene un tipo, cada objeto conoce su tipo, es imposible usar accidental o deliberadamente un objeto de un tipo “como si” fuera un objeto de un tipo diferente , y todas las operaciones elementales en el objeto son delegado a su tipo.

Esto no tiene nada que ver con los nombres . Un nombre en Python no “tiene un tipo”: si y cuando se define un nombre, el nombre se refiere a un objeto , y el objeto tiene un tipo (pero eso no obliga a un tipo en el nombre : a nombre es un nombre).

Un nombre en Python puede referirse perfectamente a diferentes objetos en diferentes momentos (como en la mayoría de los lenguajes de progtwigción, aunque no en todos), y no existe una restricción en el nombre que, si alguna vez se ha referido a un objeto de tipo X, por lo tanto, siempre está restringido para referirse solo a otros objetos de tipo X. Las restricciones en los nombres no son parte del concepto de “tipificación fuerte”, aunque algunos entusiastas de la tipificación estática (donde los nombres se restringen, y en una estática, AKA comstackción tiempo, moda, también) hacen mal uso del término de esta manera.

Las otras respuestas han hecho un buen trabajo explicando la tipificación del pato y la respuesta simple por tzot :

Python no tiene variables, como otros idiomas donde las variables tienen un tipo y un valor; Tiene nombres que apuntan a objetos, que conocen su tipo.

Sin embargo , una cosa interesante ha cambiado desde 2010 (cuando se hizo la pregunta por primera vez), a saber, la implementación de PEP 3107 (implementada en Python 3). Ahora puede especificar realmente el tipo de un parámetro y el tipo del tipo de retorno de una función como este:

 def pick(l: list, index: int) -> int: return l[index] 

Aquí podemos ver que pick toma 2 parámetros, una lista l y un index entero. También debe devolver un entero.

Así que aquí está implícito que l es una lista de enteros que podemos ver sin mucho esfuerzo, pero para funciones más complejas puede ser un poco confuso en cuanto a lo que debe contener la lista. También queremos que el valor predeterminado del index sea ​​0. Para resolver esto, puede optar por escribir una pick como esta:

 def pick(l: "list of ints", index: int = 0) -> int: return l[index] 

Tenga en cuenta que ahora colocamos una cadena como el tipo de l , que está permitido sintácticamente, pero no es bueno para el análisis progtwigdo (al que volveremos más adelante).

Es importante tener en cuenta que Python no generará un TypeError si pasa un flotante al index , la razón de esto es uno de los puntos principales en la filosofía de diseño de Python: “Todos aceptamos adultos aquí” , lo que significa que usted es Se espera que sea consciente de lo que puede pasar a una función y de lo que no puede. Si realmente desea escribir código que isinstance TypeErrors, puede usar la función isinstance para verificar que el argumento pasado sea del tipo adecuado o una subclase de este tipo:

 def pick(l: list, index: int = 0) -> int: if not isinstance(l, list): raise TypeError return l[index] 

En la siguiente sección y en los comentarios se explica más sobre por qué rara vez debería hacer esto y qué debería hacer en su lugar.

PEP 3107 no solo mejora la legibilidad del código sino que también tiene varios casos de uso de ajustes que puede leer aquí .


La anotación de tipo atrajo mucha más atención en Python 3.5 con la introducción de PEP 484, que introduce un módulo estándar para sugerencias de tipo.

Estos consejos de tipo provienen del mypy del verificador de tipo ( GitHub ), que ahora es compatible con PEP 484 .

Con el módulo de escritura viene con una colección bastante completa de sugerencias de tipo, que incluyen:

  • List , Tuple , Set , Map : para list , tuple , set y map respectivamente.
  • Iterable – útil para generadores.
  • Any – cuando podría ser cualquier cosa.
  • Union : cuando podría ser cualquier cosa dentro de un conjunto específico de tipos, a diferencia de Any .
  • Optional – cuando podría ser Ninguno. Taquigrafía para la Union[T, None] .
  • TypeVar – usado con generics.
  • Callable : se utiliza principalmente para funciones, pero se puede usar para otros callables.

Estos son los consejos de tipo más comunes. Puede encontrar una lista completa en la documentación del módulo de mecanografía .

Este es el ejemplo anterior que utiliza los métodos de anotación introducidos en el módulo de escritura:

 from typing import List def pick(l: List[int], index: int) -> int: return l[index] 

Una característica poderosa es Callable que le permite escribir métodos de anotación que toman una función como un argumento. Por ejemplo:

 from typing import Callable, Any, Iterable def imap(f: Callable[[Any], Any], l: Iterable[Any]) -> List[Any]: """An immediate version of map, don't pass it any infinite iterables!""" return list(map(f, l)) 

El ejemplo anterior podría ser más preciso con el uso de TypeVar lugar de Any , pero esto se ha dejado como un ejercicio para el lector ya que creo que ya he completado mi respuesta con demasiada información sobre las nuevas características maravillosas habilitadas por las sugerencias de tipo .


Anteriormente, cuando se documentaba un código Python con, por ejemplo, Sphinx, algunas de las funciones anteriores se podían obtener escribiendo cadenas de documentos con este formato:

 def pick(l, index): """ :param l: list of integers :type l: list :param index: index at which to pick an integer from *l* :type index: int :returns: integer at *index* in *l* :rtype: int """ return l[index] 

Como puede ver, esto requiere una cantidad de líneas adicionales (el número exacto depende de qué tan explícito quiera ser y de cómo formatee su cadena de documentos). Pero ahora debería estar claro cómo el PEP 3107 proporciona una alternativa que es superior en muchos (¿todos?). Esto es especialmente cierto en combinación con PEP 484 que, como hemos visto, proporciona un módulo estándar que define una syntax para estos tipos de anotaciones / anotaciones que se pueden usar de tal manera que sean inequívocas y precisas y, al mismo tiempo, flexibles. poderosa combinacion

En mi opinión personal, esta es una de las mejores características de Python. No puedo esperar a que la gente empiece a aprovechar el poder de la misma. Lo siento por la respuesta larga, pero esto es lo que sucede cuando me emociono.


Un ejemplo de código de Python que utiliza en gran medida sugerencias de tipo se puede encontrar aquí .

No especificas un tipo. El método solo fallará (en tiempo de ejecución) si intenta acceder a atributos que no están definidos en los parámetros que se pasan.

Así que esta simple función:

 def no_op(param1, param2): pass 

… no fallará sin importar qué dos argumentos se pasen.

Sin embargo, esta función:

 def call_quack(param1, param2): param1.quack() param2.quack() 

… fallará en el tiempo de ejecución si param1 y param2 no tienen atributos invocables llamados quack .

Muchos idiomas tienen variables, que son de un tipo específico y tienen un valor. Python no tiene variables; tiene objetos, y usted usa nombres para referirse a estos objetos.

En otros idiomas, cuando dices:

 a = 1 

entonces una variable (típicamente entera) cambia su contenido al valor 1.

En Python,

 a = 1 

significa “usar el nombre a para referirse al objeto 1 “. Puedes hacer lo siguiente en una sesión interactiva de Python:

 >>> type(1)  

El type función se llama con el objeto 1 ; ya que cada objeto conoce su tipo, es fácil para el type descubrir dicho tipo y devolverlo.

Igualmente, cada vez que definas una función.

 def funcname(param1, param2): 

la función recibe dos objetos y los denomina param1 y param2 , independientemente de sus tipos. Si desea asegurarse de que los objetos recibidos sean de un tipo específico, codifique su función como si fuera del tipo (s) necesario (s) y detecte las excepciones que se lanzan si no lo son. Las excepciones lanzadas son típicamente TypeError ( TypeError una operación no válida) y AttributeError (intentaste acceder a un miembro inexistente (los métodos también son miembros)).

Python no está fuertemente tipado en el sentido de la comprobación de tipos estática o de tiempo de comstackción.

La mayoría del código de Python se incluye en la llamada “Escritura de pato” ; por ejemplo, busca un método read en un objeto. No le importa si el objeto es un archivo en el disco o un socket, solo desea leer N bytes de ella.

Como explica Alex Martelli ,

La solución preferida normal, de Pythonic es casi invariablemente “tipificación de pato”: intente usar el argumento como si fuera de un determinado tipo deseado, hágalo en una statement try / except que detecte todas las excepciones que podrían surgir si el argumento no fuera en realidad de ese tipo (o de cualquier otro tipo bien simulado por el pato ;-), y en la cláusula de excepción, pruebe con otra cosa (usando el argumento “como si” fuera de otro tipo).

Lee el rest de su publicación para obtener información útil.

A Python no le importa lo que pasas a sus funciones. Cuando llama a my_func(a,b) , las variables param1 y param2 mantendrán los valores de a y b. Python no sabe que está llamando a la función con los tipos adecuados, y espera que el progtwigdor se encargue de eso. Si se llamará a su función con diferentes tipos de parámetros, puede envolver el código accediendo a ellos con los bloques try / except y evaluar los parámetros de la forma que desee.

Nunca especificas el tipo; Python tiene el concepto de tipificación de pato ; Básicamente, el código que procesa los parámetros hará ciertas suposiciones sobre ellos, tal vez al llamar a ciertos métodos que se espera que un parámetro implemente. Si el parámetro es del tipo incorrecto, se lanzará una excepción.

En general, depende de su código asegurarse de que esté transmitiendo objetos del tipo adecuado; no hay un comstackdor que lo haga cumplir antes de tiempo.

Hay una notoria excepción de la tipificación de patos que vale la pena mencionar en esta página.

Cuando la función str llama al método de clase __str__ , verifica sutilmente su tipo:

 >>> class A(object): ... def __str__(self): ... return 'a','b' ... >>> a = A() >>> print a.__str__() ('a', 'b') >>> print str(a) Traceback (most recent call last): File "", line 1, in  TypeError: __str__ returned non-string (type tuple) 

Como si Guido nos indicara qué excepción debería generar un progtwig si encuentra un tipo inesperado.

En Python todo tiene un tipo. Una función de Python hará todo lo que se le pida que haga si el tipo de argumentos lo admite.

Ejemplo: foo agregará todo lo que puede ser __add__ ed;) sin preocuparse mucho por su tipo. Así que eso significa que, para evitar el fracaso, debe proporcionar solo las cosas que admiten la adición.

 def foo(a,b): return a + b class Bar(object): pass class Zoo(object): def __add__(self, other): return 'zoom' if __name__=='__main__': print foo(1, 2) print foo('james', 'bond') print foo(Zoo(), Zoo()) print foo(Bar(), Bar()) # Should fail 

No vi esto mencionado en otras respuestas, así que lo añadiré a la olla.

Como han dicho otros, Python no impone el tipo en los parámetros de función o método. Se supone que sabes lo que estás haciendo y que, si realmente necesitas saber el tipo de algo que se transmitió, lo verificará y decidirá qué hacer por ti mismo.

Una de las herramientas principales para hacer esto es la función isinstance ().

Por ejemplo, si escribo un método que espera obtener datos de texto binario sin procesar, en lugar de las cadenas codificadas en utf-8 normales, podría verificar el tipo de parámetros en el camino y adaptarme a lo que encuentre, o elevar una excepción a rechazar.

 def process(data): if not isinstance(data, bytes) and not isinstance(data, bytearray): raise TypeError('Invalid type: data must be a byte string or bytearray, not %r' % type(data)) # Do more stuff 

Python también proporciona todo tipo de herramientas para cavar en objetos. Si eres valiente, incluso puedes usar importlib para crear tus propios objetos de clases arbitrarias sobre la marcha. Hice esto para recrear objetos a partir de datos JSON. Tal cosa sería una pesadilla en un lenguaje estático como C ++.