Différences entre isinstance()
et type()
en Python?
Vérification de type avec
isinstance(obj, Base)
permet des instances de sous-classes et de multiples bases possibles:
isinstance(obj, (Base1, Base2))
tandis que la vérification de type avec
type(obj) is Base
prend uniquement en charge le type référencé.
En tant que sidenote, is
est probablement plus approprié que
type(obj) == Base
parce que les classes sont des singletons.
Évitez la vérification de type - utilisez le polymorphisme (typage de canard)
En Python, vous voulez généralement autoriser n'importe quel type pour vos arguments, le traiter comme prévu, et si l'objet ne se comporte pas comme prévu, il générera une erreur appropriée. Ceci est connu sous le nom de polymorphisme, également connu sous le nom de typage de canard.
def function_of_duck(duck):
duck.quack()
duck.swim()
Si le code ci-dessus fonctionne, nous pouvons supposer que notre argument est un canard. Ainsi, nous pouvons passer dans d'autres choses sont de réels sous-types de canard:
function_of_duck(mallard)
ou qui fonctionnent comme un canard:
function_of_duck(object_that_quacks_and_swims_like_a_duck)
et notre code fonctionne toujours.
Cependant, dans certains cas, il est souhaitable d'effectuer une vérification de type explicite. Vous avez peut-être des choses sensées à faire avec différents types d'objets. Par exemple, l'objet Pandas Dataframe peut être construit à partir de dictés ou d' enregistrements. Dans un tel cas, votre code doit savoir quel type d'argument il obtient pour pouvoir le gérer correctement.
Donc, pour répondre à la question:
Différences entre isinstance()
et type()
en Python?
Permettez-moi de démontrer la différence:
type
Supposons que vous devez garantir un certain comportement si votre fonction obtient un certain type d'argument (un cas d'utilisation courant pour les constructeurs). Si vous vérifiez un type comme celui-ci:
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 nous essayons de transmettre un dict qui est une sous-classe de dict
(comme nous devrions pouvoir, si nous nous attendons à ce que notre code suive le principe de la substitution de Liskov , que les sous-types puissent être substitués aux types), notre code casse!:
from collections import OrderedDict
foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))
soulève une erreur!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
ValueError: argument must be a dict
isinstance
Mais si nous utilisons isinstance
, nous pouvons soutenir la substitution 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')]))
Retour OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])
Classes de base abstraites
En fait, nous pouvons faire encore mieux. collections
fournit des classes de base abstraites qui appliquent des protocoles minimaux pour différents types. Dans notre cas, si nous n'attendons que le Mapping
protocole, nous pouvons faire ce qui suit, et notre code devient encore plus 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
Réponse au commentaire:
Il convient de noter que le type peut être utilisé pour comparer plusieurs classes en utilisant type(obj) in (A, B, C)
Oui, vous pouvez tester l'égalité des types, mais au lieu de ce qui précède, utilisez les bases multiples pour le flux de contrôle, sauf si vous autorisez spécifiquement ces types uniquement:
isinstance(obj, (A, B, C))
La différence, encore une fois, est qu'elle isinstance
prend en charge les sous-classes qui peuvent être substituées au parent sans autrement casser le programme, une propriété connue sous le nom de substitution Liskov.
Encore mieux, cependant, inversez vos dépendances et ne vérifiez pas du tout les types spécifiques.
Conclusion
Donc, comme nous voulons prendre en charge la substitution de sous-classes, dans la plupart des cas, nous voulons éviter la vérification de type avec type
et préférer la vérification de type avec isinstance
- à moins que vous n'ayez vraiment besoin de connaître la classe précise d'une instance.
str
etunicode
où vous pouvez simplement vérifierbasestring
), vous pouvez utiliser un tuple pour comparer plusieurs types. Pour vérifier sisomething
c'estint
oustr
utiliserisinstance(something, (int, str))
.