Quais são as diferenças entre type () e isinstance ()?

1249

Quais são as diferenças entre esses dois fragmentos de código?

Usando type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

Usando isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()
abade
fonte
Nota: Se não estiver stre unicode(onde você pode apenas verificar basestring), você pode usar uma tupla para verificar vários tipos. Para verificar se somethingé intou strusar isinstance(something, (int, str)).
xuiqzy

Respostas:

1271

Para resumir o conteúdo de outras respostas (já boas!), isinstanceAtende a herança (uma instância de uma classe derivada também é uma instância de uma classe base), enquanto a verificação da igualdade de typenão (exige identidade de tipos e rejeita instâncias) subtipos, subclasses AKA).

Normalmente, em Python, você deseja que seu código ofereça suporte a herança, é claro (uma vez que a herança é muito útil, seria ruim interromper o código usando o seu!), Portanto, isinstanceé menos ruim do que verificar a identidade de types, porque suporta perfeitamente herança.

Não isinstanceé bom , lembre-se - é menos ruim do que verificar a igualdade de tipos. A solução preferida normal, pitonica, é quase sempre "digitar pato": tente usar o argumento como se fosse de um determinado tipo desejado, faça-o em uma instrução try/ exceptcapturando todas as exceções que possam surgir se o argumento não for de fato aquele type (ou qualquer outro tipo que imite o pato ;-) e, na exceptcláusula, tente outra coisa (usando o argumento "como se" fosse de outro tipo).

basestring é , no entanto, um caso bastante especial - um tipo interno que existe apenas para permitir o uso isinstance(ambos stre unicodesubclasse basestring). Strings são sequências (você pode fazer um loop sobre elas, indexá-las, cortá-las, ...), mas geralmente você deseja tratá-las como tipos "escalares" - é um pouco incoveniente (mas um caso de uso razoavelmente frequente) tratar todos os tipos de strings (e talvez outros tipos escalares, ou seja, aqueles em que você não pode executar um loop) de uma maneira, todos os contêineres (listas, conjuntos, dictos, ...) de outra maneira, e basestringmais o isinstanceajudam a fazer isso - a estrutura geral deste idioma é algo como:

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

Você poderia dizer que basestringé uma Classe Base Abstrata ("ABC") - ela não oferece funcionalidade concreta para subclasses, mas existe como um "marcador", principalmente para uso com isinstance. O conceito é obviamente crescente no Python, desde que o PEP 3119 , que introduz uma generalização, foi aceito e foi implementado a partir do Python 2.6 e 3.0.

O PEP deixa claro que, embora os ABCs possam substituir a digitação de patos, geralmente não há grande pressão para fazer isso (veja aqui ). Os ABCs implementados nas versões recentes do Python, no entanto, oferecem vantagens extras: isinstance(e issubclass) agora podem significar mais do que apenas "[uma instância de] uma classe derivada" (em particular, qualquer classe pode ser "registrada" com um ABC para que mostrar como uma subclasse e suas instâncias como instâncias do ABC); e os ABCs também podem oferecer comodidade extra às subclasses reais de maneira muito natural, por meio de aplicativos de padrão de design do Método de Modelo (veja aqui e aqui [[parte II]] para obter mais informações sobre o TM DP, em geral e especificamente em Python, independente dos ABCs). .

Para a mecânica subjacente do suporte ABC, conforme oferecido no Python 2.6, veja aqui ; para a versão 3.1, muito parecida, veja aqui . Nas duas versões, as coleções de módulos de biblioteca padrão (que é a versão 3.1 - para a versão 2.6 muito semelhante, veja aqui ) oferecem vários ABCs úteis.

Para os fins desta resposta, o principal a ser retido sobre os ABCs (além de um posicionamento mais natural para a funcionalidade TM DP, em comparação com a alternativa clássica do Python de classes mixin , como UserDict.DictMixin ), é que elas produzem isinstance(e issubclass) muito mais atraente e difundido (no Python 2.6 e em diante) do que costumava ser (no 2.5 e antes) e, portanto, por outro lado, torna a verificação da igualdade de tipos uma prática ainda pior nas versões recentes do Python do que já era.

Alex Martelli
fonte
9
- Não é que a instituição seja boa, lembre-se - é menos ruim do que verificar a igualdade de tipos. A solução preferida normal, pitonica, é quase sempre "digitar pato". Essa é uma visão bastante limitada: existem casos muito bons para usar isinstance () em, por exemplo, um intérprete em que os tipos refletem a gramática. Ser "pitonico" não é tudo!
Gene Callahan
2
basestring não está disponível no Python 3.
erobertc 9/10/19
@GeneCallahan, porque existem casos muito bons, não significa que o que foi dito não é uma boa regra geral. Concordo que a verificação antecipada do tipo definitivamente tem seu lugar, mas deixar os patos grasnados deve cobrir a maioria dos casos de maneira mais flexível e eficiente.
Eric Ed Lohmar
@erobertc, de acordo com o que há de novo no Python 3.0 , "O tipo abstrato de cadeia de base interno foi removido. Use str em vez disso".
neurite
345

Aqui está um exemplo em que isinstanceobtém algo que typenão pode:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

Nesse caso, um objeto de caminhão é um veículo, mas você verá o seguinte:

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.

Em outras palavras, também isinstanceé válido para subclasses.

Veja também: Como comparar o tipo de um objeto no Python?

Pedro
fonte
143
porque há casos em que você não deseja o comportamento isInstance, eu argumentaria que não há "melhor". Eles apenas fazem algo diferente.
precisa saber é
27
-1, porque "isinstance é melhor que o tipo" é um comentário enganoso. entende-se como " typeestá obsoleto, use em isinstancevez disso" à primeira vista. por exemplo, o que eu queria era exatamente type()verificar, mas fui enganado por um curto período de tempo (e tive que depurar um pouco) por esse motivo.
30815 Ceremcem
8
É um bom exemplo de como eles funcionam de maneira diferente, mas acabei de encontrar um caso em que eu especificamente precisava type()e não isinstance(). Um não é melhor; eles são para coisas diferentes.
EL_DON 30/04
103

Diferenças entre isinstance()e type()em Python?

Verificação de tipo com

isinstance(obj, Base)

permite instâncias de subclasses e várias bases possíveis:

isinstance(obj, (Base1, Base2))

Considerando que a verificação de tipo com

type(obj) is Base

suporta apenas o tipo referenciado.


Como nota de rodapé, isé provavelmente mais apropriado do que

type(obj) == Base

porque as aulas são singletons.

Evite a verificação de tipo - use Polimorfismo (digitação de pato)

Em Python, geralmente você deseja permitir qualquer tipo para seus argumentos, tratá-lo como esperado e, se o objeto não se comportar conforme o esperado, isso gerará um erro apropriado. Isso é conhecido como polimorfismo, também conhecido como digitação de pato.

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

Se o código acima funcionar, podemos presumir que nosso argumento é um pato. Assim, podemos passar em outras coisas são sub-tipos reais de pato:

function_of_duck(mallard)

ou que funcionam como um pato:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

e nosso código ainda funciona.

No entanto, há alguns casos em que é desejável verificar explicitamente o tipo. Talvez você tenha coisas sensatas a fazer com diferentes tipos de objetos. Por exemplo, o objeto Pandas Dataframe pode ser construído a partir de dictos ou registros. Nesse caso, seu código precisa saber que tipo de argumento está sendo recebido, para que ele possa manipulá-lo adequadamente.

Então, para responder à pergunta:

Diferenças entre isinstance()e type()em Python?

Permita-me demonstrar a diferença:

type

Digamos que você precise garantir um certo comportamento se sua função receber um certo tipo de argumento (um caso de uso comum para construtores). Se você verificar um 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')

Se tentarmos passar um ditado que é uma subclasse de dict(como deveríamos ser, se esperamos que nosso código siga o princípio da substituição de Liskov , que subtipos podem ser substituídos por tipos), nosso código quebra !:

from collections import OrderedDict

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

gera um erro!

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
ValueError: argument must be a dict

isinstance

Mas se usarmos isinstance, podemos apoiar a Substituição 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')]))

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

Classes base abstratas

De fato, podemos fazer ainda melhor. collectionsfornece classes básicas abstratas que impõem protocolos mínimos para vários tipos. No nosso caso, se esperamos apenas o Mappingprotocolo, podemos fazer o seguinte e nosso código se torna ainda mais flexível:

from collections import Mapping

def foo(a_dict):
    if not isinstance(a_dict, Mapping):
        raise ValueError('argument must be a dict')
    return a_dict

Resposta ao comentário:

Deve-se notar que o tipo pode ser usado para verificar várias classes usando type(obj) in (A, B, C)

Sim, você pode testar a igualdade de tipos, mas, em vez do acima, use as várias bases para o fluxo de controle, a menos que você esteja especificamente permitindo apenas esses tipos:

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

A diferença, novamente, é que isinstancesuporta subclasses que podem ser substituídas pelo pai sem interromper o programa, uma propriedade conhecida como substituição de Liskov.

Melhor ainda, inverta suas dependências e não verifique tipos específicos.

Conclusão

Portanto, como queremos oferecer suporte à substituição de subclasses, na maioria dos casos, queremos evitar a verificação de typetipo e preferir a verificação de tipo isinstance- a menos que você realmente precise conhecer a classe precisa de uma instância.

Aaron Hall
fonte
Se você possui o seu_module.py onde você procura isinstance(instance, y)e usa from v.w.x import y, e importa essa verificação, mas quando instancia instanceo uso, em from x import yvez de como foi importado no seu_module.py, a verificação isinstance falhará, mesmo que seja da mesma classe.
toonarmycaptain
64

O último é preferido, porque ele manipulará subclasses corretamente. De fato, seu exemplo pode ser escrito ainda mais facilmente, porque isinstance()o segundo parâmetro pode ser uma tupla:

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

ou, usando a basestringclasse abstract:

if isinstance(b, basestring):
    do_something_else()
John Millikin
fonte
9

Uma diferença prática de uso é como eles lidam com booleans:

Truee Falsesão apenas palavras-chave que significam 1e 0em python. Portanto,

isinstance(True, int)

e

isinstance(False, int)

ambos retornam True. Ambos os booleanos são uma instância de um número inteiro. type(), no entanto, é mais inteligente:

type(True) == int

retorna False.

Alec Alameddine
fonte
0

Para as diferenças reais, podemos encontrá-lo code, mas não consigo encontrar o implemento do comportamento padrão do arquivo isinstance().

No entanto, podemos obter o semelhante abc .__ instancecheck__ de acordo com __instancecheck__ .

De cima abc.__instancecheck__, depois de usar o teste abaixo:

# 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

Eu chego a esta conclusão, 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)})

BTW: melhor não misturar uso relative and absolutely import, use absolutely importde project_dir (adicionado por sys.path)

Cheney
fonte