str insistence example python oop inheritance types

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



isinstance python 3 example (6)

¿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 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.