python - insistence - ¿Cuáles son las diferencias entre type() y isinstance()?
isinstance python 3 example (6)
¿Diferencias entre
isinstance()
ytype()
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 probable que sea 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 polimorfismo, 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 los subtipos actuales 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 todavía funciona.
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 "<stdin>", line 1, in <module>
File "<stdin>", 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 programa, 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.
¿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()
Aquí es por qué isinstance
es mejor que type
:
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 también para las subclases.
Ver también: ¿Cómo comparar el tipo de un objeto en Python?
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()
Para las diferencias reales, podemos encontrarlo en el code
, pero no puedo encontrar el implemento del comportamiento predeterminado de isinstance()
.
Sin embargo, podemos obtener el similar abc.__instancecheck__ 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))
<class ''aaa.aa.b''> <class ''aa.b''> <class ''aaa.aa.b''>
<class ''aaa.aa.b''> __subclasses__ []
<class ''aaa.aa.b''> __mro__ (<class ''aaa.aa.b''>, <class ''object''>)
<class ''aaa.aa.b''> __subclasshook__ NotImplemented
<class ''aaa.aa.b''> __subclasshook__ NotImplemented
<class ''aa.b''> __subclasses__ []
<class ''aa.b''> __mro__ (<class ''aa.b''>, <class ''object''>)
<class ''aa.b''> __subclasshook__ NotImplemented
<class ''aa.b''> __subclasshook__ NotImplemented
<class ''object''> __subclasses__ [..., <class ''aaa.aa.b''>, <class ''aa.b''>]
<class ''object''> __mro__ (<class ''object''>,)
<class ''object''> __subclasshook__ NotImplemented
<class ''object''> __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: es mejor no mezclar el uso relative and absolutely import
, usar absolutely import
desde project_dir (agregado por sys.path
)
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 evitar que 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 comprobar la igualdad de tipos. La solución preferida normal, Pythonic es casi invariablemente "tipificación de pato": intente usar el argumento como si fuera de un determinado tipo deseado, hágalo en una declaración 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 (utilizando el argumento "como si" fuera de otro tipo).
basestring
es , sin embargo, un caso bastante especial: un tipo incorporado que existe solo para permitirle usar isinstance
(tanto str
como la subclase de subclase de basestring
). 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 puedes hacer un bucle) de una manera, todos los contenedores (listas, conjuntos, dictos, ...) de otra manera, y basestring
plus isinstance
te 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 basestring
es una Clase Base Abstracta ("ABC"): no ofrece ninguna funcionalidad concreta para las subclases, sino que existe como un "marcador", principalmente para usar con isinstance
. El concepto es obviamente 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 claro que, si bien los ABC pueden sustituir a la tipificación de pato, generalmente no existe una gran presión para hacerlo (ver here ). 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 solo "[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 here y here [[parte II]] para obtener más información sobre el TM DP, en general y específicamente en Python, independientemente del ABC) .
Para conocer la mecánica subyacente del soporte de ABC tal como se ofrece en Python 2.6, consulte here ; Para su versión 3.1, muy similar, ver here . En ambas versiones, las collections módulos de biblioteca estándar (que es la versión 3.1, para una versión 2.6 muy similar, consulte here ) 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 UserDict.DictMixin 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.
Según la documentación de Python, aquí hay una declaración:
8.15. tipos - nombres para los tipos incorporados
A partir de Python 2.2, las funciones incorporadas de fábrica como
int()
ystr()
también son nombres para los tipos correspondientes.
Por lo isinstance()
debe preferir isinstance()
sobre type()
.