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. collections
fornece classes básicas abstratas que impõem protocolos mínimos para vários tipos. No nosso caso, se esperamos apenas o Mapping
protocolo, 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 isinstance
suporta 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 type
tipo e preferir a verificação de tipo isinstance
- a menos que você realmente precise conhecer a classe precisa de uma instância.
str
eunicode
(onde você pode apenas verificarbasestring
), você pode usar uma tupla para verificar vários tipos. Para verificar sesomething
éint
oustr
usarisinstance(something, (int, str))
.