¿Cuál es la forma canónica de verificar el tipo en Python?

¿Cuál es la mejor manera de verificar si un objeto dado es de un tipo dado? ¿Qué hay de verificar si el objeto se hereda de un tipo dado?

Digamos que tengo un objeto o . ¿Cómo puedo comprobar si es un str ?

Para verificar si o es una instancia de str o cualquier subclase de str , use isinstance (esta sería la forma “canónica”):

 if isinstance(o, str): 

Para verificar si el tipo de o es exactamente str (excluir subclases):

 if type(o) is str: 

Lo siguiente también funciona, y puede ser útil en algunos casos:

 if issubclass(type(o), str): 

Consulte Funciones incorporadas en la Referencia de la biblioteca de Python para obtener información relevante.

Una nota más: en este caso, si estás usando python 2, es posible que desees usar:

 if isinstance(o, basestring): 

porque esto también capturará las cadenas Unicode ( unicode no es una subclase de str ; tanto str como unicode son subclases de basestring ). Tenga en cuenta que basestring ya no existe en Python 3, donde hay una separación estricta de cadenas ( str ) y datos binarios ( bytes ).

Alternativamente, isinstance acepta una tupla de clases. Esto devolverá True si x es una instancia de cualquier subclase de cualquiera de (str, unicode):

 if isinstance(o, (str, unicode)): 

La forma más pythonica de verificar el tipo de un objeto es … no comprobarlo.

Dado que Python recomienda el uso de Duck Typing , solo debe try...except usar los métodos del objeto de la manera que desee. Entonces, si su función está buscando un objeto de archivo grabable, no compruebe que es una subclase de file , ¡intente usar su método .write() !

Por supuesto, a veces, estas bonitas abstracciones se descomponen y lo que necesita es la isinstance(obj, cls) . Pero usar con moderación.

isinstance(o, str) devolverá True si o es un str o es de un tipo que hereda de str .

type(o) is str devolverá True si y solo si o es un str. Devolverá False si o es de un tipo que hereda de str .

Después de que se formuló y respondió la pregunta, se agregaron sugerencias de tipo a Python . Las sugerencias de tipo en Python permiten que los tipos se verifiquen, pero de una manera muy diferente a los idiomas tipificados estáticamente. Las sugerencias de tipo en Python asocian los tipos de argumentos esperados con las funciones como datos accesibles en tiempo de ejecución asociados con las funciones y esto permite verificar los tipos. Ejemplo de syntax de tipo de sugerencia:

 def foo(i: int): return i foo(5) foo('oops') 

En este caso, queremos que se genere un error para foo('oops') ya que el tipo anotado del argumento es int . La sugerencia de tipo agregada no causa un error cuando el script se ejecuta normalmente. Sin embargo, agrega atributos a la función que describe los tipos esperados que otros progtwigs pueden consultar y usar para verificar errores de tipo.

Uno de estos otros progtwigs que se pueden usar para encontrar el error de tipo es mypy :

 mypy script.py script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int" 

(Es posible que deba instalar mypy desde su administrador de paquetes. No creo que venga con CPython, pero parece tener cierto nivel de “oficialidad”).

La comprobación de tipos de esta manera es diferente de la comprobación de tipos en lenguajes comstackdos tipificados estáticamente. Debido a que los tipos son dynamics en Python, la verificación de tipos debe realizarse en tiempo de ejecución, lo que impone un costo, incluso en los progtwigs correctos, si insistimos en que ocurra en cada oportunidad. Las comprobaciones explícitas de tipo también pueden ser más restrictivas de lo necesario y causar errores innecesarios (por ejemplo, ¿el argumento realmente debe ser exactamente del tipo de list o es algo lo suficientemente iterable?).

La ventaja de la comprobación explícita de tipos es que puede detectar errores antes y dar mensajes de error más claros que la tipificación de pato. Los requisitos exactos de un tipo de pato solo pueden expressse con documentación externa (es de esperar que sea exhaustiva y precisa) y los errores de tipos incompatibles pueden ocurrir lejos de donde se originan.

Las sugerencias de tipo de Python están destinadas a ofrecer un compromiso donde los tipos se pueden especificar y verificar, pero no hay costo adicional durante la ejecución del código habitual.

El paquete de typing ofrece variables de tipo que se pueden usar en sugerencias de tipo para express los comportamientos necesarios sin necesidad de tipos particulares. Por ejemplo, incluye variables como Iterable y Callable para que las sugerencias especifiquen la necesidad de cualquier tipo con esos comportamientos.

Si bien las sugerencias de tipo son la forma más Pythonic de verificar tipos, a menudo es incluso más Pythonic no verificar los tipos y depender de la tipificación de pato. Las sugerencias de tipo son relativamente nuevas y el jurado aún está deliberando sobre cuándo son la solución más Pythonic. Una comparación relativamente incontrovertida pero muy general: las sugerencias de tipo proporcionan una forma de documentación que se puede aplicar, permiten que el código se genere antes y es más fácil de entender los errores, puede detectar errores que la tipificación no puede y se puede verificar de forma estática (de forma inusual). sentido pero todavía está fuera del tiempo de ejecución). Por otro lado, la tipificación de pato ha sido la forma de Pythonic durante mucho tiempo, no impone la sobrecarga cognitiva de tipificación estática, es menos detallada y aceptará todos los tipos viables y algunos.

Aquí hay un ejemplo de por qué el patear es malo sin saber cuándo es peligroso. Por ejemplo: aquí está el código Python (posiblemente omitiendo la sangría adecuada), tenga en cuenta que esta situación se puede evitar cuidando las funciones de instancia y de subclase para asegurarse de que cuando realmente necesita un pato, no reciba una bomba.

 class Bomb: def __init__(self): "" def talk(self): self.explode() def explode(self): print "BOOM!, The bomb explodes." class Duck: def __init__(self): "" def talk(self): print "I am a duck, I will not blow up if you ask me to talk." class Kid: kids_duck = None def __init__(self): print "Kid comes around a corner and asks you for money so he could buy a duck." def takeDuck(self, duck): self.kids_duck = duck print "The kid accepts the duck, and happily skips along" def doYourThing(self): print "The kid tries to get the duck to talk" self.kids_duck.talk() myKid = Kid() myBomb = Bomb() myKid.takeDuck(myBomb) myKid.doYourThing() 
 isinstance(o, str) 

Enlace a documentos

Creo que lo mejor de usar un lenguaje dynamic como Python es que realmente no deberías tener que revisar algo así.

Simplemente llamaría a los métodos requeridos en su objeto y atraparía un AttributeError . Más adelante, esto le permitirá llamar a sus métodos con otros objetos (aparentemente no relacionados) para realizar diferentes tareas, como burlarse de un objeto para probar.

He usado esto mucho cuando urllib2.urlopen() datos de la web con urllib2.urlopen() que devuelve un archivo como objeto. A su vez, esto se puede pasar a casi cualquier método que se lea de un archivo, porque implementa el mismo método read() que un archivo real.

Pero estoy seguro de que hay un momento y un lugar para usar isinstance() , de lo contrario probablemente no estaría allí 🙂

A Hugo:

Probablemente te refieras a la list lugar de a la array , pero eso apunta a todo el problema con la verificación de tipos: no quieres saber si el objeto en cuestión es una lista, quieres saber si es algún tipo de secuencia o si es una sola objeto. Así que trata de usarlo como una secuencia.

Digamos que desea agregar el objeto a una secuencia existente, o si es una secuencia de objetos, agréguelos todos

 try: my_sequence.extend(o) except TypeError: my_sequence.append(o) 

Un truco con esto es si está trabajando con cadenas y / o secuencias de cadenas; eso es complicado, ya que una cadena se suele considerar como un solo objeto, pero también es una secuencia de caracteres. Peor que eso, ya que es realmente una secuencia de cadenas de una sola longitud.

Por lo general, elijo diseñar mi API para que solo acepte un solo valor o una secuencia, lo que facilita las cosas. No es difícil poner un [ ] alrededor de su valor único cuando lo pasa si es necesario.

(Aunque esto puede causar errores con las cadenas, ya que se ven como (son) secuencias).

Para validaciones de tipo más complejas, me gusta el enfoque de typeguard de validación basado en anotaciones de sugerencias de tipo python:

 from typeguard import check_type from typing import List try: check_type('mylist', [1, 2], List[int]) except TypeError as e: print(e) 

Puede realizar validaciones muy complejas de manera muy limpia y legible.

 check_type('foo', [1, 3.14], List[Union[int, float]]) # vs isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

Puede verificar con la línea de abajo para ver qué tipo de carácter es el valor dado:

 def chr_type(chrx): if chrx.isalpha()==True: return 'alpha' elif chrx.isdigit()==True: return 'numeric' else: return 'nothing' chr_type("12)