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


1249

¿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()

Nota: Si no lo es stry unicode(donde solo puede verificar basestring), puede usar una tupla para verificar contra varios tipos. Para comprobar si somethinges into strusar isinstance(something, (int, str)).
xuiqzy

Respuestas:


1271

Para resumir el contenido de otras respuestas (¡ya buenas!), isinstanceAtiende la herencia (una instancia de una clase derivada también es una instancia de una clase base), mientras que verifica la igualdad de typeno (exige la identidad de los tipos y rechaza las instancias de subtipos, subclases de AKA).

Normalmente, en Python, desea que su código admita la herencia, por supuesto (dado que la herencia es muy útil, sería malo evitar que el código que usa el suyo lo use), por lo que isinstancees menos malo que verificar la identidad de types porque admite perfectamente herencia.

Eso no isinstancees bueno , eso sí, es menos malo que verificar la igualdad de tipos. La solución normal, Pythonic, preferida es casi invariablemente "escribir pato": intente usar el argumento como si fuera de un cierto tipo deseado, hágalo en una declaración try/ exceptcapturando todas las excepciones que podrían surgir si el argumento no fuera en realidad tipo (o cualquier otro tipo muy bien imitándolo ;-), y en la exceptcláusula, intente otra cosa (usando el argumento "como si" fuera de otro tipo).

basestring es , sin embargo, un caso bastante especial: un tipo integrado que existe solo para permitirle usar isinstance(ambos stry la unicodesubclase basestring). Las cadenas son secuencias (puede recorrerlas, indexarlas, dividirlas, ...), pero generalmente desea tratarlas como tipos "escalares"; es algo incómodo (pero un caso de uso razonablemente frecuente) para tratar todo tipo de cadenas (y tal vez otros tipos escalares, es decir, aquellos en los que no puede hacer un bucle) de una manera, todos los contenedores (listas, conjuntos, dictos, ...) de otra manera, y basestringademás lo isinstanceayuda a hacer eso: la estructura general de este el 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 basestringes una clase base abstracta ("ABC"): no ofrece una funcionalidad concreta para las subclases, sino que existe como un "marcador", principalmente para su uso isinstance. El concepto obviamente está creciendo en Python, ya que PEP 3119 , que presenta una generalización del mismo, fue aceptado y se implementó a partir de Python 2.6 y 3.0.

El PEP deja en claro que, si bien los ABC a menudo pueden sustituir la tipificación de patos, generalmente no existe una gran presión para hacerlo (ver aquí ). Sin embargo, el ABC implementado en versiones recientes de Python ofrece extras: isinstance(y issubclass) ahora puede significar más que simplemente "[una instancia de] una clase derivada" (en particular, cualquier clase puede "registrarse" con un ABC para que sea mostrar como una subclase y sus instancias como instancias de ABC); y ABC también puede ofrecer una conveniencia adicional a las subclases reales de una manera muy natural a través de las aplicaciones de patrones de diseño de Método de plantilla (vea aquí y aquí [[parte II]] para obtener más información sobre TM DP, en general y específicamente en Python, independientemente de ABC) .

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

Para el propósito de esta respuesta, la clave para retener sobre ABC (más allá de una ubicación posiblemente más natural para la funcionalidad TM DP, en comparación con la alternativa clásica Python de clases mixin como UserDict.DictMixin ) es que hacen isinstance(y 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, por el contrario, hacer 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.


99
"No es que la instancia sea buena, eso sí, es menos malo que verificar la igualdad de tipos". La solución normal, Pythonic, preferida es casi invariablemente "tipear patos". Esta es una visión bastante limitada: hay muy buenos casos para usar isinstance () en, por ejemplo, un intérprete donde los tipos reflejan la gramática. ¡Ser "pitón" no lo es todo!
Gene Callahan

2
basetring no está disponible en Python 3.
erobertc

@GeneCallahan, porque hay muy buenos casos, no significa que lo que se dijo no sea una buena regla general. Estoy de acuerdo en que verificar el tipo con anticipación definitivamente tiene su lugar, pero dejar que los patos graznen debería cubrir la mayoría de los casos de manera más flexible y eficiente.
Eric Ed Lohmar

@erobertc, de acuerdo con What's New in Python 3.0 , "Se eliminó el tipo de resumen de cadena base incorporado. Utilice str en su lugar".
neurita

345

Aquí hay un ejemplo donde isinstancelogra algo que typeno puede:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

en este caso, un objeto de camión es un Vehículo, pero obtendrá 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, también isinstancees cierto para las subclases.

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


143
porque hay un caso en el que no quieres el comportamiento isInstance, diría que no hay "mejor". Simplemente hacen algo diferente.
philgo20

27
-1, porque "isinstance es mejor que type" es un comentario engañoso. se entiende como " typeestá en desuso, use isinstanceen su lugar" a primera vista. por ejemplo, lo que quería era type()verificar exactamente , pero me engañaron por un corto tiempo (y tuve que depurar un poco) por esa razón.
ceremcem

8
Es un buen ejemplo de cómo funcionan de manera diferente, pero me encontré con un caso en el que específicamente lo necesitaba type()y no isinstance(). Uno no es mejor; Son para diferentes cosas.
EL_DON

103

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

Verificación de tipo con

isinstance(obj, Base)

permite instancias de subclases y múltiples bases posibles:

isinstance(obj, (Base1, Base2))

mientras que la verificación de tipo con

type(obj) is Base

solo admite el tipo al que se hace referencia.


Como nota al margen, ises probable que sea más apropiado que

type(obj) == Base

porque las clases son singletons.

Evite la verificación de tipos: utilice el polimorfismo (tipificación de pato)

En Python, por lo general, desea permitir cualquier tipo de argumentos, tratarlo como se espera y, si el objeto no se comporta como se espera, generará un error apropiado. Esto se conoce como polimorfismo, también conocido como tipeo 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 son subtipos reales de pato:

function_of_duck(mallard)

o que funcionan como un pato:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

y nuestro código aún funciona.

Sin embargo, hay algunos casos en los que es conveniente hacer una verificación de tipo explícita. Quizás tenga cosas sensatas 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 poder manejarlo adecuadamente.

Entonces, para responder la pregunta:

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

Permítame demostrar la diferencia:

type

Digamos que necesita garantizar un cierto comportamiento si su función obtiene un cierto tipo de argumento (un caso de uso común para los constructores). Si marca 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 tratamos de 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 , que los subtipos pueden ser sustituidos 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')]))

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

Clases base abstractas

De hecho, podemos hacerlo aún mejor. collectionsproporciona clases base abstractas que aplican protocolos mínimos para varios tipos. En nuestro caso, si solo esperamos el Mappingprotocolo, 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:

Debe tenerse en cuenta que el tipo se puede usar para verificar varias clases usando type(obj) in (A, B, C)

Sí, puede probar la igualdad de tipos, pero en lugar de lo anterior, use las bases múltiples 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 isinstanceadmite subclases que pueden ser sustituidas por el padre sin interrumpir el programa, una propiedad conocida como sustitución de Liskov.

Sin embargo, aún mejor, invierta sus dependencias y no compruebe ningún tipo específico.

Conclusión

Entonces, dado que queremos admitir la sustitución de subclases, en la mayoría de los casos, queremos evitar la verificación de tipo con typey preferimos la verificación de tipo con isinstance, a menos que realmente necesite conocer la clase precisa de una instancia.


Si tiene your_module.py donde verifica isinstance(instance, y)y usa from v.w.x import y, e importa esa verificación, pero cuando crea una instancia de instanceuso en from x import ylugar de cómo se importó y en your_module.py, la verificación de instancia fallará, aunque sea la misma clase.
toonarmycaptain

64

Se prefiere este último porque manejará las subclases correctamente. De hecho, su ejemplo se puede escribir aún más fácilmente porque isinstance()el segundo parámetro puede ser una tupla:

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

o, usando la basestringclase abstracta:

if isinstance(b, basestring):
    do_something_else()


9

Una diferencia práctica de uso es cómo manejan booleans:

Truey Falseson solo palabras clave que significan 1y 0en python. Así,

isinstance(True, int)

y

isinstance(False, int)

ambos regresan True. Ambos booleanos son una instancia de un entero. type(), sin embargo, es más inteligente:

type(True) == int

vuelve False.


0

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

Sin embargo, podemos obtener una 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))

<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

Llego a esta conclusión, para type:

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

Para 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, use absolutely importfrom project_dir (agregado por sys.path)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.