Quelles sont les différences entre type () et isinstance ()?

1249

Quelles sont les différences entre ces deux fragments de code?

En utilisant type():

import types

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

En utilisant isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()
abbé
la source
Remarque: Si ce n'est pas le cas ( stret unicodeoù vous pouvez simplement vérifier basestring), vous pouvez utiliser un tuple pour comparer plusieurs types. Pour vérifier si somethingc'est intou strutiliser isinstance(something, (int, str)).
xuiqzy

Réponses:

1271

Pour résumer le contenu d'autres réponses (déjà bonnes!), L' isinstancehéritage (une instance d'une classe dérivée est également une instance d'une classe de base), tout en vérifiant l'égalité de typene pas (il exige l'identité des types et rejette les instances de sous-types, sous-classes AKA).

Normalement, en Python, vous voulez que votre code prenne en charge l'héritage, bien sûr (puisque l'héritage est si pratique, il serait mauvais d'arrêter le code utilisant le vôtre de l'utiliser!), Donc isinstancec'est moins mauvais que de vérifier l'identité de types car il prend en charge de manière transparente héritage.

Ce n'est pas isinstanceest bon , l' esprit vous-ce est juste moins mauvais que de vérifier l' égalité des types. La solution normale, Pythonique, préférée est presque invariablement le "typage du canard": essayez d'utiliser l'argument comme s'il était d'un certain type souhaité, faites-le dans une instruction try/ en exceptattrapant toutes les exceptions qui pourraient survenir si l'argument n'était pas en fait de cela type (ou tout autre type imitant bien le canard ;-), et dans la exceptclause, essayez autre chose (en utilisant l'argument "comme si" c'était d'un autre type).

basestring est , cependant, un cas assez spécial - un type intégré qui existe uniquement pour vous permettre d'utiliser isinstance(les deux stret la unicodesous - classe basestring). Les chaînes sont des séquences (vous pouvez les boucler, les indexer, les découper, ...), mais vous voulez généralement les traiter comme des types "scalaires" - il est quelque peu peu pratique (mais un cas d'utilisation assez fréquent) de traiter toutes sortes de chaînes (et peut-être d'autres types scalaires, c'est-à-dire celles sur lesquelles vous ne pouvez pas boucler) d'une manière, tous les conteneurs (listes, ensembles, dict, ...) d'une autre manière, et basestringplus isinstancevous aide à le faire — la structure globale de cette idiome est quelque chose comme:

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

On pourrait dire qu'il basestrings'agit d'une classe de base abstraite ("ABC") - elle n'offre aucune fonctionnalité concrète aux sous-classes, mais existe plutôt en tant que "marqueur", principalement pour une utilisation avec isinstance. Le concept est évidemment en pleine croissance en Python, puisque PEP 3119 , qui en introduit une généralisation, a été accepté et a été implémenté à partir de Python 2.6 et 3.0.

Le PEP indique clairement que, si les ABC peuvent souvent remplacer le typage du canard, il n'y a généralement pas de grande pression pour le faire (voir ici ). Les ABC tels qu'implémentés dans les versions récentes de Python offrent cependant des avantages supplémentaires: isinstance(et issubclass) peuvent désormais signifier plus que "[une instance de] une classe dérivée" (en particulier, n'importe quelle classe peut être "enregistrée" avec un ABC afin qu'elle montrer comme une sous-classe, et ses instances comme des instances de l'ABC); et les ABC peuvent également offrir une commodité supplémentaire aux sous-classes réelles de manière très naturelle via les applications de modèle de conception de la méthode de modèle (voir ici et ici [[partie II]] pour en savoir plus sur le TM DP, en général et spécifiquement en Python, indépendant des ABC) .

Pour les mécanismes sous-jacents de la prise en charge ABC proposés dans Python 2.6, voir ici ; pour leur version 3.1, très similaire, voir ici . Dans les deux versions, les collections de modules de bibliothèque standard (c'est la version 3.1 - pour la version 2.6 très similaire, voir ici ) offrent plusieurs ABC utiles.

Aux fins de cette réponse, l'élément clé à retenir sur les ABC (au-delà d'un placement sans doute plus naturel pour la fonctionnalité TM DP, par rapport à l'alternative classique Python des classes de mixage telles que UserDict.DictMixin ) est qu'ils font isinstance(et issubclass) beaucoup plus attrayants et omniprésents (dans Python 2.6 et versions ultérieures) qu'auparavant (dans les versions 2.5 et antérieures), et par conséquent, en revanche, la vérification de l'égalité des types est une pratique encore pire dans les versions récentes de Python qu'elle ne l'était déjà.

Alex Martelli
la source
9
«Ce n'est pas que l'instance soit bonne, ne vous en faites pas, c'est juste moins mauvais que de vérifier l'égalité des types. La solution normale, pythonique, préférée est presque invariablement le "typage du canard". Ceci est une vue assez limitée: il y a de très bons cas pour utiliser isinstance () dans, disons, un interpréteur où les types reflètent la grammaire. Être "Pythonic" n'est pas tout!
Gene Callahan
2
basestring n'est pas disponible dans Python 3.
erobertc
@GeneCallahan, parce qu'il y a de très bons cas, ne signifie pas que ce qui a été dit n'est pas une bonne règle générale. Je suis d'accord que la vérification du type à l'avance a définitivement sa place, mais laisser les canards charlatans devrait couvrir la plupart des cas de manière plus flexible et efficace.
Eric Ed Lohmar
@erobertc, selon Quoi de neuf dans Python 3.0 , "Le type abstrait de basestring intégré a été supprimé. Utilisez plutôt str."
Neurite
345

Voici un exemple où isinstanceréalise quelque chose qui typene peut pas:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

dans ce cas, un objet camion est un véhicule, mais vous obtiendrez ceci:

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 d'autres termes, cela isinstancevaut également pour les sous-classes.

Voir aussi: Comment comparer le type d'un objet en Python?

Peter
la source
143
parce qu'il y a des cas où vous ne voulez pas le comportement isInstance, je dirais qu'il n'y a pas de "meilleur". Ils font juste quelque chose de différent.
philgo20
27
-1, car "isinstance is better than type" est un commentaire trompeur. il est compris comme " typeest obsolète, utilisez isinstanceplutôt" au premier coup d'œil. par exemple, ce que je voulais était de type()vérifier exactement , mais j'ai été induit en erreur pendant une courte période (et j'ai dû déboguer un peu) pour cette raison.
ceremcem
8
C'est un bon exemple de la façon dont ils fonctionnent différemment, mais je suis juste tombé sur un cas où j'avais spécifiquement besoin type()et non isinstance(). On n'est pas mieux; ils sont pour des choses différentes.
EL_DON
103

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, isest 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. collectionsfournit des classes de base abstraites qui appliquent des protocoles minimaux pour différents types. Dans notre cas, si nous n'attendons que le Mappingprotocole, 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 isinstanceprend 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 typeet 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.

Aaron Hall
la source
Si vous avez votre_module.py où vous vérifiez isinstance(instance, y)et utilisez from v.w.x import y, et que vous importez cette vérification, mais lorsque vous instanciez instancevous utilisez à la from x import yplace de la façon dont y a été importé dans votre_module.py, la vérification isinstance échouera, même si c'est la même classe.
toonarmycaptain
64

Ce dernier est préféré, car il gérera correctement les sous-classes. En fait, votre exemple peut être écrit encore plus facilement car isinstance()le deuxième paramètre de peut être un tuple:

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

ou, en utilisant la basestringclasse abstraite:

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

Une différence d'utilisation pratique est la façon dont ils gèrent booleans:

Trueet ne Falsesont que des mots clés qui signifient 1et 0en python. Donc,

isinstance(True, int)

et

isinstance(False, int)

les deux reviennent True. Les deux booléens sont une instance d'un entier. type(), cependant, est plus intelligent:

type(True) == int

retourne False.

Alec Alameddine
la source
0

Pour les vraies différences, nous pouvons le trouver dans code, mais je ne trouve pas l'implémentation du comportement par défaut du isinstance().

Cependant, nous pouvons obtenir le même abc .__ instancecheck__ selon __instancecheck__ .

D'en haut abc.__instancecheck__, après avoir utilisé le test ci-dessous:

# 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

Je reçois cette conclusion, pour type:

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
type(INSTANCE) ~= INSTANCE.__class__
type(CLASS) ~= CLASS.__class__

Pour 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: mieux vaut ne pas mélanger l'utilisation relative and absolutely import, utiliser absolutely importdepuis project_dir (ajouté par sys.path)

Cheney
la source