Pregunta ¿Cuáles son las diferencias entre type () e 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()

933
2017-10-11 03:50


origen


Respuestas:


Para resumir los contenidos de otras respuestas (¡ya son buenas!) isinstance abastece a la herencia (una instancia de una clase derivada es un instancia de una clase base, también), mientras se busca la igualdad de type no (exige identidad de tipos y rechaza instancias de subtipos, subclases AKA).

Normalmente, en Python, quieres que tu código sea compatible con la herencia, por supuesto (¡ya que la herencia es muy útil, sería malo detener el código usando el tuyo para usarlo!), Entonces isinstance es menos malo que verificar la identidad de types porque admite sin problemas la herencia.

No es eso isinstance es bueno, fíjate, es solo menos mal que verificar la igualdad de tipos. La solución normal, Pythonic, preferida es casi invariablemente "tipado de pato": intente utilizar el argumento como si era de cierto tipo deseado, hazlo en un try/except una declaración que capte todas las excepciones que podrían surgir si el argumento no fuera de hecho de ese tipo (o cualquier otro tipo que simule bien el pato ;-), y en el except cláusula, intente con otra cosa (utilizando el argumento "como si" fuera de algún otro tipo).

basestring  es, sin embargo, es un caso bastante especial: un tipo incorporado que existe solamente para dejarte usar isinstance (ambos str y unicode subclase basestring) Las cadenas son secuencias (puedes recorrerlas, indexarlas, cortarlas, ...), pero generalmente quieres tratarlas como "escalares"; es algo incoveniente (pero un caso de uso razonablemente frecuente) para tratar todo tipo de cadenas (y tal vez otros tipos escalares, es decir, aquellas sobre las que no se puede repetir) de una manera, todos los contenedores (listas, conjuntos, dictos, ...) de otra manera, y basestring más isinstance te ayuda a hacer eso; la estructura general de este idioma es algo así como:

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

Podrías decirlo basestring es un 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 obviamente está creciendo en Python, ya que PEP 3119, que introduce una generalización del mismo, fue aceptado y se implementó comenzando con Python 2.6 y 3.0.

El PEP deja en claro que, aunque el ABC a menudo puede sustituir al tipado de pato, generalmente no hay una gran presión para hacerlo (ver aquí) Sin embargo, los ABC implementados en las versiones recientes de Python ofrecen extras adicionales: isinstance (y issubclass) ahora puede significar algo más que "[una instancia de] una clase derivada" (en particular, cualquier clase puede ser "registrada" con un ABC para que se muestre como una subclase, y sus instancias como instancias del ABC); y los ABC también pueden ofrecer 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 (ver aquí y aquí [[parte II]] para obtener más información sobre el TM DP, en general y específicamente en Python, independientemente del ABC).

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

A los efectos de esta respuesta, es clave retener el ABC (más allá de una ubicación posiblemente más natural para la funcionalidad de TM DP, en comparación con la alternativa clásica de Python de las clases de mixin, como UserDict.DictMixin) es que hacen isinstance (y issubclass) mucho más atractivos y generalizados (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, hacen que la comprobación de igualdad de tipo sea una práctica aún peor en versiones recientes de Python de lo que solía ser .


988
2017-10-11 04:31



Este es el 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á 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, también.

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


260
2017-10-11 03:58



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

Tipo de verificación con

isinstance(obj, Base)

permite instancias de subclases y múltiples bases posibles:

isinstance(obj, (Base1, Base2))

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 más apropiado que

type(obj) == Base

porque las clases son singletons

Evite la verificación de tipos: use polimorfismo (pato-tipado)

En Python, por lo general, desea permitir cualquier tipo para sus argumentos, tratarlo como se espera, y si el objeto no se comporta como se esperaba, generará un error apropiado. Esto se conoce como polimorfismo, también conocido como pato-tipado.

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

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

function_of_duck(mallard)

o que funciona 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 comprobar el tipo de forma 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 dicts o archivos. En tal caso, su código necesita saber qué tipo de argumento está obteniendo para que pueda manejarlo adecuadamente.

Entonces, para responder la pregunta:

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

Permíteme demostrar la diferencia:

type

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

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

Si tratamos de pasar en un dict que es una subclase de dict (como deberíamos) si esperamos que nuestro código siga el principio de Liskov Sustitución, que los subtipos pueden ser sustituidos por tipos) ¡nuestro código se rompe !:

from collections import OrderedDict

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

provoca 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 imponen 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 para comentar:

Cabe señalar que el tipo se puede utilizar para verificar contra múltiples clases utilizando type(obj) in (A, B, C)

Sí, puedes probar la igualdad de tipos, pero en lugar de lo anterior, usa las múltiples bases para el flujo de control, a menos que solo específicamente estés permitiendo esos tipos:

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

La diferencia, de nuevo, es que isinstance admite subclases que pueden sustituirse por el elemento primario sin romper el programa, una propiedad conocida como sustitución de Liskov.

Aún mejor, sin embargo, invierta sus dependencias y no verifique para tipos específicos en absoluto.

Conclusión

Entonces, dado que queremos apoyar la sustitución de subclases, en la mayoría de los casos, queremos evitar la verificación de tipos con type y prefiere la verificación de tipo con isinstance - a menos que realmente necesite saber la clase precisa de una instancia.


60
2018-02-06 04:13



Este último es el preferido, ya que manejará las subclases de forma adecuada. De hecho, tu ejemplo puede escribirse 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 el basestring clase abstracta:

if isinstance(b, basestring):
    do_something_else()

55
2017-10-11 03:54



De acuerdo con la documentación de Python aquí hay una declaración:

8.15. tipos - Nombres para tipos incorporados

Comenzando en Python 2.2, incorporado   funciones de fábrica tales como int() y    str() también son nombres para el   tipos correspondientes

Asi que isinstance() debe ser preferido sobre type().


11
2017-10-11 03:59



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

Sin embargo, podemos obtener el similar abc .__ instanciacheck__ de acuerdo a __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

Entiendo esta conclusión, por 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, utilizar absolutely import from project_dir (agregado por sys.path)


0
2018-05-10 08:00