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

1278

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

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

Herge
fuente
77
Bueno, el enfoque canónico en Python es no verificar el tipo en absoluto (a menos que esté depurando). Por lo general, solo intenta usarlo como una cadena (por ejemplo, concatenar con otras cadenas, imprimir en la consola, etc.); Si cree que puede fallar, use try / except o hasattr. Dicho esto, la respuesta aceptada es la forma canónica de hacer lo que generalmente "no debería hacer" en el mundo de Python. Para obtener más información, busque en Google "Python duck typing" o lea estos: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs
99
Creo que el Sr. Coombs está pasando por alto ejemplos como clases serializables que no son JSON. Si coloca una gran cantidad de datos a través de una función (cuyo código no se puede influir), es posible que desee convertir ciertas partes de esos datos en, por ejemplo, un <str> antes de pasarlo. Al menos esa es la forma en que terminó en esta página ...
John Carrell
2
Parece que la razón más común para pedir esto es que uno quiere distinguir entre cadenas e iterables de cadenas. Esta es una pregunta difícil porque las cadenas son iterables de cadenas: una cadena de un solo carácter es incluso una secuencia de sí misma (la última vez que lo verifiqué, probablemente no debería confiar en ella). ¿Pero alguien alguna vez lo usaría para algo parecido a una cuerda? . Entonces, la respuesta a "¿Qué debo hacer para distinguir entre cadenas y otros iterables de cadenas?" es correctamente: "Depende de lo que intentes hacer". :-D
clacke
2
Las anotaciones de tipo Python ahora son una cosa. Echa un vistazo a mypy
Sheena

Respuestas:

1522

Para verificar si oes una instancia stro alguna subclase de str, use isinstance (esta sería la forma "canónica"):

if isinstance(o, str):

Para verificar si el tipo de oes 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á usando Python 2, es posible que desee usar:

if isinstance(o, basestring):

porque esto también capturará cadenas Unicode ( unicodeno es una subclase de str; ambas stry unicodeson subclases de basestring). Tenga en cuenta que basestringya no existe en Python 3, donde hay una separación estricta de cadenas ( str) y datos binarios ( bytes).

Alternativamente, isinstanceacepta una tupla de clases. Esto devolverá Truesi oes una instancia de cualquier subclase de cualquiera de (str, unicode):

if isinstance(o, (str, unicode)):
Fredrik Johansson
fuente
31
str .__ subclasses __ () solo devuelve las subclases directas de str, y no hace lo mismo que issubclass () o isinstance (). (Para hacer eso, tendrías que llamar recursivamente .__ subclases __ ().
Thomas Wouters
15
Esta es una buena respuesta, pero creo que realmente debería comenzar con una advertencia de que generalmente no debería estar haciendo esto en Python. Tal como está, parece validar la suposición de que esto es "algo canónico que hacer en Python", lo cual no es así.
Jon Coombs
44
Estas son respuestas de python2. Por ejemplo, no hay una cadena base en python3.
dfrankow
44
¿Cuál es la diferencia entre instancia y "exactamente"? Si type(a) is Objectentonces no es cierto también eso isinstance(a, Object). Sin embargo, si type(a) is SubClassOfObject, entonces type(a) is Object == False, pero isinstance(a, Object) == True. ¿Derecha?
mavavilj
1
@mavavilj: a is bsignifica que ayb son exactamente lo mismo, es decir, referencias a la misma entidad en la memoria. Entonces, ay btendría que ser exactamente la misma clase, no subclases, como con isinstance(). Ver por ejemplo stackoverflow.com/a/133024/1072212
Terry Brown
196

La forma más pitónica de verificar el tipo de un objeto es ... no verificarlo.

Dado que Python alienta a Duck Typing , solo debe try...exceptusar los métodos del objeto de la forma en que desea usarlos. Entonces, si su función está buscando un objeto de archivo grabable, no verifique que sea una subclase de file, ¡solo intente usar su .write()método!

Por supuesto, a veces estas agradables abstracciones se descomponen y isinstance(obj, cls)es lo que necesitas. Pero use con moderación.

Dan Lenski
fuente
75
En mi humilde opinión, la forma más pitónica es hacer frente a cualquier argumento que se da. En mi código, a menudo no puedo saber si recibo un objeto o una matriz de objetos, y uso la verificación de tipos internamente para convertir un solo objeto en una lista de un elemento.
sastanin
14
En lugar de tratar de usar su método de escritura, hay momentos en que desea hacer esto sin causar una excepción. En este caso, podría hacer ... if hasattr(ob, "write") and callable(ob.write): O guardar algún acceso dict ...func = getattr(ob, "write", None) if callable(func): ...
ideasman42
142
La escritura de pato se trata de usar una biblioteca. La verificación de tipos se trata de escribir una biblioteca. No es el mismo problema de dominio.
RickyA
16
@RickyA, no estoy de acuerdo. La escritura de pato se trata de interactuar con objetos utilizando interfaces con semánticas bien conocidas. Esto puede aplicarse al código de la biblioteca o al código que usa dicha biblioteca.
Dan Lenski
66
@ nyuszika7h, en Python3 hasattrsolo suprime un AttributeError - Ver: docs.python.org/3.4/library/functions.html#hasattr
ideasman42
57

isinstance(o, str)regresará Truesi oes stro es de un tipo que hereda de str.

type(o) is strvolverá Truesi y solo si oes un str. Volverá Falsesi oes de un tipo que hereda de str.

Herge
fuente
66
Por supuesto, esto fallará si el objeto no es una instancia de 'str', sino de algo parecido a una cadena. Como unicode, mmap, UserString o cualquier otro tipo definido por el usuario. El enfoque habitual en Python es no hacer verificaciones de tipo.
Thomas Wouters
66
No tiene que disculparse, está bien responder su propia pregunta. SO es para las respuestas, no para el karma.
Eli Bendersky
2
Esto es muy útil. Porque la diferencia entre isinstancey type(var) == type('')no está clara.
sastanin
30

Después de hacer y responder la pregunta, se agregaron sugerencias de tipo a Python . Las sugerencias de tipo en Python permiten que se verifiquen los tipos, pero de una manera muy diferente a los lenguajes estáticos. Las sugerencias de tipo en Python asocian los tipos de argumentos esperados con funciones como datos accesibles en tiempo de ejecución asociados con funciones y esto permite que se verifiquen los tipos. Ejemplo de sintaxis de sugerencia de tipo:

def foo(i: int):
    return i

foo(5)
foo('oops')

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

Uno de estos otros programas 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 necesite instalar mypydesde su administrador de paquetes. No creo que venga con CPython pero parece tener cierto nivel de "oficialidad").

La verificación de tipos de esta manera es diferente de la verificación de tipos en lenguajes compilados de tipo estático. Debido a que los tipos son dinámicos en Python, la verificación de tipos debe realizarse en tiempo de ejecución, lo que impone un costo, incluso en los programas correctos, si insistimos en que suceda en cada oportunidad. Las verificaciones de tipo explícitas también pueden ser más restrictivas de lo necesario y causar errores innecesarios (por ejemplo, ¿es necesario que el argumento sea exactamente del listtipo o es algo iterable suficiente?).

La ventaja de la verificación explícita de tipos es que puede detectar errores antes y dar mensajes de error más claros que la escritura de pato. Los requisitos exactos de un tipo de pato solo se pueden expresar 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 en el que los tipos se pueden especificar y verificar, pero no hay ningún costo adicional durante la ejecución de código habitual.

El typingpaquete ofrece variables de tipo que se pueden usar en sugerencias de tipo para expresar los comportamientos necesarios sin requerir tipos particulares. Por ejemplo, incluye variables como Iterabley Callablepara sugerencias para especificar la necesidad de cualquier tipo con esos comportamientos.

Si bien las sugerencias de tipo son la forma más pitónica de verificar los tipos, a menudo es aún más pitónico no verificar los tipos en absoluto y confiar en la escritura de pato. Las sugerencias de tipo son relativamente nuevas y el jurado aún no sabe cuándo son la solución más pitónica. Una comparación relativamente poco controvertida pero muy general: las sugerencias de tipo proporcionan una forma de documentación que se puede hacer cumplir, permiten que el código genere errores anteriores y más fáciles de entender, puede detectar errores que no se pueden escribir en el pato y se pueden verificar de forma estática (en un caso inusual sentido, pero todavía está fuera del tiempo de ejecución). Por otro lado, el tipeo de pato ha sido la forma pitónica durante mucho tiempo, no impone la sobrecarga cognitiva del tipeo estático, es menos detallado y aceptará todos los tipos viables y más.

Praxeolítico
fuente
2
-1: mypy específicamente se llama a sí mismo un "verificador de tipo estático", por lo que no estoy seguro de dónde obtuviste "la verificación de tipo debe hacerse en tiempo de ejecución".
Kevin
@Kevin En retrospectiva, esa fue una digresión innecesaria, pero para profundizar más, las sugerencias de tipo de Python se convierten en datos de tiempo de ejecución y mypyes un módulo de Python que se utiliza importlibpara acceder a esos datos. Si esto es una "verificación de tipo estático" es una pregunta filosófica, pero es diferente de lo que la mayoría esperaría ya que el intérprete de lenguaje normal y la maquinaria de importación están involucrados.
Praxeolítico
44
Eso tampoco es cierto. Se utiliza typed_ast, que a su vez es sólo un clon de AST con características adicionales. ast no importa módulos; los analiza en un árbol de sintaxis abstracta.
Kevin
18

Aquí hay un ejemplo de por qué escribir pato 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 isinstance y issubclassof para asegurarse de que cuando realmente necesite un pato, no obtenga 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()
Dmitry
fuente
36
Incluso con la verificación de tipos, puede crear class EvilDuck(Duck)y anular talk (). O más probablemente, class ChineseCancerDuck(Duck)con un efecto secundario desagradable que no aparece hasta años después. Sería mejor supervisar a tu hijo (y probar a fondo sus juguetes :)
Brett Thomas
36
Las bombas no hablan. No agregue métodos sin sentido y esto no sucederá.
derecha el
77
@Dmitry, esta es la crítica común de Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... básicamente estás diciendo que cualquier interfaz para la que el lenguaje no impone la semántica es mala. Creo que este es más el enfoque de Java. El punto principal de la escritura de pato de Python es que solo funciona cuando hay una convención comúnmente confirmada sobre lo que significan las interfaces específicas. Por ejemplo, podría descifrar una gran cantidad de código Python al anular el __file__atributo (comúnmente utilizado para identificar objetos similares a archivos) para que signifique algo más.
Dan Lenski
2
Todo esto se reduce a la vieja broma "Doctor, me duele cuando hago esto". ... "Entonces no hagas eso". Insatisfactorio para alguien que está acostumbrado a "si se compila, se ejecuta", pero es por eso que la obsesión por las pruebas surgió del mundo del lenguaje dinámico.
clacke
1
@clacke básicamente, es demasiado costoso imponer tipos en tiempo de ejecución estrictamente porque TODO debe ser un objeto (para mapear de una cadena a cualquier tipo posible), y demasiado conveniente para no tener tipeado porque el tipeado permite técnicas de prototipos realmente poderosas que superan cosas que normalmente son muy difíciles de hacer con interfaces rígidas. Además, cualquier lenguaje estático se enfrenta a un punto en el que necesita crear tipeo de pato a través de bibliotecas dinámicas, evaluación y stringificación, o interfaces, y estas cosas no lo hacen inherentemente malo, solo muy poderoso.
Dmitry
12
isinstance(o, str)

Enlace a documentos

Alexander Kojevnikov
fuente
1
Si bien este enlace puede responder la pregunta, es mejor incluir aquí las partes esenciales de la respuesta y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
EKons
7

Creo que lo bueno de usar un lenguaje dinámico como Python es que realmente no deberías tener que verificar algo así.

Simplemente llamaría a los métodos requeridos en su objeto y capturarí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 probarlo.

Lo he usado mucho cuando obtengo datos de la web con los urllib2.urlopen()que devuelve un archivo como objeto. Esto a su vez se puede pasar a casi cualquier método que lea de un archivo, ya que implementa el mismo read()método que un archivo real.

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

Will Harding
fuente
Un buen ejemplo de cuándo debe usarlo es si está analizando un objeto json dinámico. No sabe de antemano si un campo es una cadena o un diccionario.
Gris
6

Para validaciones de tipos más complejas, me gusta el enfoque de validación de typeguard 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 una 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) 
Granitosaurio
fuente
6

Puede verificar el tipo de una variable usando __name__ de un tipo.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
Yerramsetty Rohit
fuente
Gracias, este es el código secreto que quería cuando lo mostraba como comentario para el usuario. Me tomó demasiado tiempo encontrar esto ...
Aaron D. Marasco
5

A Hugo:

Probablemente quieras decir en listlugar dearray , pero eso apunta al problema completo 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 un solo objeto. Intenta usarlo como una secuencia.

Supongamos 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 a menudo se considera 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 valor único o una secuencia, lo que facilita las cosas. No es difícil poner un [ ]valor único alrededor de usted cuando lo pasa si es necesario.

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

Chris Barker
fuente
0

Una forma simple de verificar el tipo es compararlo con algo cuyo tipo conoces.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Ultrablendz
fuente
-1

Creo que la mejor manera es escribir bien sus variables. Puede hacer esto utilizando la biblioteca "escribiendo".

Ejemplo:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

Ver https://docs.python.org/3/library/typing.html

cherah30
fuente
-7

Puede verificar con la línea a continuación para verificar 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)
Venkatesan
fuente
3
¿Seguro que no quieres eliminar esta respuesta @Venkatesan?
Gris