web-dev-qa-db-fra.com

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

Quelles sont les différences entre ces deux fragments de code? Utiliser type():

import types

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

Utiliser isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()
1137
abbot

Pour résumer le contenu d'autres réponses (déjà bonnes!), isinstance prend en charge l'hé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 type ne le fait pas (il demande l’identité des types et rejette les instances de sous-types, les 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’empêcher le code de l’utiliser!), Donc isinstance est moins dommageable que de vérifier l’identité de types car il supporte l'héritage de manière transparente.

Ce n'est pas que isinstance soit bon , remarquez, c'est juste moins mauvais que de vérifier l'égalité des types. La solution privilégiée normale, Pythonic, est presque invariablement le "dactylographie": essayez d’utiliser l’argument comme si c’était du type souhaité, faites-le dans un try/except instruction interceptant toutes les exceptions qui pourraient survenir si l'argument n'était pas en fait de ce type (ou de tout autre type, bien imitant canard ;-), et dans la clause except, essayez autre chose (en utilisant l'argument "comme si" c'était d'un autre type).

basestring est , cependant, un cas assez particulier - un type prédéfini qui existe seulement pour vous permettre d'utiliser isinstance (à la fois str et unicode sous-classe basestring). Les chaînes de caractères sont des séquences (vous pouvez les parcourir, les indexer, les découper, ...), mais vous souhaitez généralement les traiter comme des types "scalaires". Il est assez peu pratique (mais un cas d'utilisation assez fréquent) de traiter toutes sortes de Les chaînes (et peut-être d'autres types scalaires, c'est-à-dire celles que vous ne pouvez pas boucler) d'une manière, tous les conteneurs (listes, ensembles, plans, ...) d'une autre manière, et basestring plus isinstance vous aide à faire cela - la structure générale de cet idiome est quelque chose comme:

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

Vous pouvez dire que basestring est 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 croissance en Python, puisque PEP 3119 , qui en introduit une généralisation, a été accepté et a été mis en œuvre à partir de Python 2.6 et 3.0.

Le PEP indique clairement que, bien que l’ABC puisse souvent se substituer à la frappe au canard, il n’ya généralement pas de forte pression pour le faire (voir ici ). Les ABC tels qu’implémentés dans les récentes versions de Python offrent toutefois des avantages supplémentaires: isinstance (et issubclass) peuvent désormais signifier plus que "[une instance de] une classe dérivée" (en En particulier, toute classe peut être "enregistrée" avec un ABC afin qu’elle apparaisse en tant que sous-classe et ses instances en tant qu’instances de ABC); et 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 Template (voir ici et ici [[partie II]]] pour plus d'informations. TM DP, en général et spécifiquement en Python, indépendamment d’ABC).

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

Pour les besoins de cette réponse, l’essentiel à retenir sur ABCs (au-delà d’un placement sans doute plus naturel pour la fonctionnalité TM DP, par rapport à l’alternative classique Python des classes mixin telles que serDict.DictMixin ) est qu'ils rendent isinstance (et issubclass) beaucoup plus attrayant et omniprésent (en Python 2.6 et en allant de l'avant) qu'ils ne l'étaient (en 2.5 et avant ), et par conséquent, au contraire, 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à.

1171
Alex Martelli

Voici un exemple où isinstance réalise quelque chose que type ne 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, isinstance est également vrai pour les sous-classes.

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

312
Peter

Différences entre isinstance() et type() en Python?

Vérification de type avec

isinstance(obj, Base)

permet d'instances de sous-classes et de multiples bases possibles:

isinstance(obj, (Base1, Base2))

alors que la vérification de type avec

type(obj) is Base

ne supporte que le type référencé.


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 souhaitez 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, une erreur appropriée sera générée. Ceci est connu sous le nom de polymorphisme, également appelé typage de canard.

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

Si le code ci-dessus fonctionne, nous pouvons présumer que notre argument est un canard. Ainsi, nous pouvons passer à autre chose sont des sous-types réels 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 de procéder à une vérification de type explicite. Peut-être avez-vous des choses sensibles à faire avec différents types d’objets. Par exemple, l'objet Pandas Dataframe peut être construit à partir d'enregistrements dict ou . 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 deviez vous assurer d'un certain comportement si votre fonction obtenait un certain type d'argument (un cas d'utilisation courant pour les constructeurs). Si vous vérifiez le type comme ceci:

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 passer un dict qui est une sous-classe de dict (comme nous devrions pouvoir le faire, si nous attendons de notre code qu'il suive le principe de Liskov Substitution , ces sous-types peut être substitué aux types) notre code rompt !:

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 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')]))

retourne 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 attendons seulement le protocole Mapping, nous pouvons procéder comme 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 est à noter que le type peut être utilisé pour vérifier plusieurs classes à l'aide de type(obj) in (A, B, C)

Oui, vous pouvez tester l'égalité des types, mais au lieu de ce qui précède, utilisez plusieurs bases pour le flux de contrôle, à moins que vous n'autorisiez spécifiquement ces types:

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

La différence, encore une fois, est que isinstance prend en charge des sous-classes pouvant être substituées au parent sans interrompre autrement le programme, propriété connue sous le nom de substitution de Liskov.

Mieux encore, inversez vos dépendances et ne recherchez aucun type spécifique.

Conclusion

Donc, puisque nous voulons supporter la substitution de sous-classes, nous voulons éviter dans la plupart des cas la vérification de type avec type et préférons la vérification de type avec isinstance - sauf si vous avez vraiment besoin de connaître la classe précise d'une instance. .

87
Aaron Hall

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

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

ou, en utilisant la classe abstraite basestring:

if isinstance(b, basestring):
    do_something_else()
61
John Millikin

Selon python documentation, voici une déclaration:

8.15. Types - Noms des types intégrés

À partir de Python 2.2, les fonctions d'usine intégrées telles que int() et str() sont également des noms pour les types correspondants.

Donc isinstance() devrait être préféré à type() .

13
Xinus

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

True et False ne sont que des mots clés qui signifient 1 et 0 en python. Ainsi,

isinstance(True, int)

et

isinstance(False, int)

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

type(True) == int

renvoie False.

1
Alec Alameddine

Pour les différences réelles, on peut le trouver dans code, mais je ne trouve pas l’implémentation du comportement par défaut de isinstance().

Cependant, nous pouvons obtenir le même type abc .__ instancecheck __ selon _ INSTANCECHECK _ .

Depuis ci-dessus 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

J'obtiens 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 de ne pas mélanger utiliser relative and absolutely import, utilisez absolutely import à partir de project_dir (ajouté par sys.path)

0
Cheney