¿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, is
es 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. collections
proporciona clases base abstractas que aplican protocolos mínimos para varios tipos. En nuestro caso, si solo esperamos el Mapping
protocolo, 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 isinstance
admite 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 type
y preferimos la verificación de tipo con isinstance
, a menos que realmente necesite conocer la clase precisa de una instancia.
str
yunicode
(donde solo puede verificarbasestring
), puede usar una tupla para verificar contra varios tipos. Para comprobar sisomething
esint
ostr
usarisinstance(something, (int, str))
.