¿Cuáles son las diferencias entre type () y isinstance ()?

¿Cuáles son las diferencias entre estos dos fragmentos de código? Utilizando type() :

 import types if type(a) is types.DictType: do_something() if type(b) in types.StringTypes: do_something_else() 

Utilizando isinstance() :

 if isinstance(a, dict): do_something() if isinstance(b, str) or isinstance(b, unicode): do_something_else() 

Para resumir el contenido de otras respuestas (¡ya son buenas!), La isinstance herencia (una instancia de una clase derivada es también una instancia de una clase base), mientras que la verificación de la igualdad de type no lo hace (exige la identidad de los tipos y rechaza instancias de subtipos, subclases AKA).

Normalmente, en Python, usted quiere que su código admita la herencia, por supuesto (ya que la herencia es muy útil, ¡sería malo detener el código que usa el suyo!), Por lo tanto, la isinstance es menos mala que verificar la identidad del type s porque perfectamente compatible con la herencia.

No es que la isinstance sea buena , claro, es menos malo que verificar la igualdad de tipos. 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 except , pruebe con otra cosa (usando el argumento “como si” fuera de otro tipo).

sin embargo, basestring es un caso bastante especial: un tipo incorporado que existe solo para permitirle usar isinstance (tanto str como basestring subclase de unicode ). Las cadenas son secuencias (puede hacer un bucle sobre ellas, indexarlas, dividirlas, …), pero generalmente desea tratarlas como tipos “escalares”; es algo incoherente (pero es un caso de uso bastante frecuente) para tratar todo tipo de cadenas (y tal vez otros tipos escalares, es decir, que no puede hacer un bucle) de una manera, todos los contenedores (listas, conjuntos, dictados, …) de otra manera, y basestring plus isinstance ayuda a hacerlo: la estructura general de este idioma es algo como:

 if isinstance(x, basestring) return treatasscalar(x) try: return treatasiter(iter(x)) except TypeError: return treatasscalar(x) 

Se podría decir que la basestring es una clase base abstracta (“ABC”); no ofrece ninguna funcionalidad concreta para las subclases, sino que existe como un “marcador”, principalmente para su uso con isinstance . El concepto es obviamente uno creciente en Python, ya que PEP 3119 , que introduce una generalización de la misma, fue aceptado y se implementó a partir de Python 2.6 y 3.0.

El PEP deja en claro que, si bien el ABC a menudo puede sustituir la tipificación de pato, generalmente no existe una gran presión para hacerlo (consulte aquí ). Sin embargo, los ABC que se implementaron en las versiones recientes de Python ofrecen isinstance adicionales: isinstance (y issubclass ) ahora puede significar más que “[una instancia de] una clase derivada” (en particular, cualquier clase puede ser “registrada” con un ABC para que se mostrará como una subclase, y sus instancias como instancias del ABC); y ABCs también puede ofrecer conveniencia adicional para las subclases reales de una manera muy natural a través de las aplicaciones de patrón de diseño de Método de Plantilla (consulte aquí y aquí [[parte II]] para obtener más información sobre el TM DP, en general y específicamente en Python, independientemente del ABCs) .

Para conocer las mecánicas subyacentes del soporte ABC tal como se ofrece en Python 2.6, vea aquí ; Para su versión 3.1, muy similar, ver aquí . En ambas versiones, las colecciones de módulos de biblioteca estándar (que es la versión 3.1, para una versión 2.6 muy similar, consulte aquí ) ofrecen varios ABCs útiles.

Para el propósito de esta respuesta, la clave para retener acerca de ABCs (más allá de una ubicación más natural para la funcionalidad de TM DP, en comparación con la alternativa clásica de Python de clases mixtas como UserDict.DictMixin ) es que hacen su isinstance (y su issubclass ) mucho más atractivo y generalizado (en Python 2.6 y en adelante) de lo que solían ser (en 2.5 y anteriores), y por lo tanto, hacen que la verificación de la igualdad de tipos sea una práctica aún peor en las versiones recientes de Python de lo que solía ser.

Aquí hay un ejemplo donde isinstance logra algo que el type no puede:

 class Vehicle: pass class Truck(Vehicle): pass 

en este caso, un objeto de camión es un Vehículo, pero obtendrás esto:

 isinstance(Vehicle(), Vehicle) # returns True type(Vehicle()) == Vehicle # returns True isinstance(Truck(), Vehicle) # returns True type(Truck()) == Vehicle # returns False, and this probably won't be what you want. 

En otras palabras, isinstance es cierto para las subclases.

Ver también: ¿Cómo comparar el tipo de un objeto en Python?

¿Diferencias entre isinstance() y type() en Python?

Tipo de comprobación con

 isinstance(obj, Base) 

Permite instancias de subclases y múltiples posibles bases:

 isinstance(obj, (Base1, Base2)) 

mientras que la comprobación de tipos con

 type(obj) is Base 

Solo soporta el tipo referenciado.


Como una nota al margen, es probablemente más apropiado que

 type(obj) == Base 

Porque las clases son singletons.

Evite la comprobación de tipos: utilice Polimorfismo (tipificación de pato)

En Python, normalmente desea permitir cualquier tipo para sus argumentos, trátelos como se esperaba y, si el objeto no se comporta como se esperaba, generará un error apropiado. Esto se conoce como polymorphism, también conocido como tipificación de pato.

 def function_of_duck(duck): duck.quack() duck.swim() 

Si el código anterior funciona, podemos suponer que nuestro argumento es un pato. Así podemos pasar en otras cosas que son los subtipos reales de pato:

 function_of_duck(mallard) 

o que funcione como un pato

 function_of_duck(object_that_quacks_and_swims_like_a_duck) 

y nuestro código sigue funcionando.

Sin embargo, hay algunos casos en los que es deseable verificar explícitamente el tipo. Tal vez tengas cosas sensibles que hacer con diferentes tipos de objetos. Por ejemplo, el objeto Pandas Dataframe se puede construir a partir de dictados o registros. En tal caso, su código necesita saber qué tipo de argumento está obteniendo para que pueda manejarlo adecuadamente.

Entonces, para responder a la pregunta:

¿Diferencias entre isinstance() y type() en Python?

Permítame demostrar la diferencia:

type

Digamos que necesita garantizar un determinado comportamiento si su función obtiene un cierto tipo de argumento (un caso de uso común para los constructores). Si compruebas un tipo como este:

 def foo(data): '''accepts a dict to construct something, string support in future''' if type(data) is not dict: # we're only going to test for dicts for now raise ValueError('only dicts are supported for now') 

Si intentamos pasar un dict que es una subclase de dict (como deberíamos poder, si esperamos que nuestro código siga el principio de la sustitución de Liskov , ese subtipo puede sustituirse por tipos) ¡nuestro código se rompe !:

 from collections import OrderedDict foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])) 

plantea un error!

 Traceback (most recent call last): File "", line 1, in  File "", line 3, in foo ValueError: argument must be a dict 

isinstance

Pero si usamos isinstance , podemos apoyar la sustitución de Liskov !:

 def foo(a_dict): if not isinstance(a_dict, dict): raise ValueError('argument must be a dict') return a_dict foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])) 

devuelve OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

Clases base abstractas

De hecho, podemos hacerlo aún mejor. collections proporciona clases básicas abstractas que imponen protocolos mínimos para varios tipos. En nuestro caso, si solo esperamos el protocolo de Mapping , podemos hacer lo siguiente, y nuestro código se vuelve aún más flexible:

 from collections import Mapping def foo(a_dict): if not isinstance(a_dict, Mapping): raise ValueError('argument must be a dict') return a_dict 

Respuesta al comentario:

Se debe tener en cuenta que el tipo se puede usar para comparar contra varias clases usando type(obj) in (A, B, C)

Sí, puede probar la igualdad de tipos, pero en lugar de lo anterior, use las múltiples bases para el flujo de control, a menos que solo permita específicamente esos tipos:

 isinstance(obj, (A, B, C)) 

La diferencia, una vez más, es que isinstance admite subclases que pueden sustituirse por las principales sin romper el progtwig, una propiedad conocida como sustitución de Liskov.

Sin embargo, aún mejor, invierta sus dependencias y no busque tipos específicos en absoluto.

Conclusión

Entonces, como queremos admitir la sustitución de subclases, en la mayoría de los casos, queremos evitar la comprobación de type con el type y preferimos la comprobación de isinstance con la isinstance , a menos que realmente necesite conocer la clase precisa de una instancia.

Este último es el preferido, porque manejará las subclases correctamente. De hecho, su ejemplo puede escribirse incluso más fácilmente porque el segundo parámetro de isinstance() puede ser una tupla:

 if isinstance(b, (str, unicode)): do_something_else() 

o, usando la clase abstracta de basestring :

 if isinstance(b, basestring): do_something_else() 

Según la documentación de Python, aquí hay una statement:

8.15. tipos – nombres para los tipos incorporados

A partir de Python 2.2, las funciones incorporadas de fábrica como int() y str() también son nombres para los tipos correspondientes.

Por lo isinstance() debe preferir isinstance() sobre type() .

Para las diferencias reales, podemos encontrarlo en el code , pero no puedo encontrar la implementación del comportamiento predeterminado de isinstance() .

Sin embargo, podemos obtener el similar abc .__ instancecheck__ de acuerdo con __instancecheck__ .

Desde arriba abc.__instancecheck__ , después de usar la prueba a continuación:

 # file tree # /test/__init__.py # /test/aaa/__init__.py # /test/aaa/aa.py class b(): pass # /test/aaa/a.py import sys sys.path.append('/test') from aaa.aa import b from aa import b as c d = b() print(b, c, d.__class__) for i in [b, c, object]: print(i, '__subclasses__', i.__subclasses__()) print(i, '__mro__', i.__mro__) print(i, '__subclasshook__', i.__subclasshook__(d.__class__)) print(i, '__subclasshook__', i.__subclasshook__(type(d))) print(isinstance(d, b)) print(isinstance(d, c))     __subclasses__ []  __mro__ (, )  __subclasshook__ NotImplemented  __subclasshook__ NotImplemented  __subclasses__ []  __mro__ (, )  __subclasshook__ NotImplemented  __subclasshook__ NotImplemented  __subclasses__ [..., , ]  __mro__ (,)  __subclasshook__ NotImplemented  __subclasshook__ NotImplemented True False 

Obtengo esta conclusión, para el type :

 # according to `abc.__instancecheck__`, they are maybe different! I have not found negative one type(INSTANCE) ~= INSTANCE.__class__ type(CLASS) ~= CLASS.__class__ 

Por isinstance :

 # guess from `abc.__instancecheck__` return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)}) 

Por cierto: mejor no mezclar el uso relative and absolutely import , usar absolutely import desde project_dir (agregado por sys.path )