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()
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 type
s 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à.
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?
Différences entre
isinstance()
ettype()
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.
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:
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')])
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
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.
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. .
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()
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()
etstr()
sont également des noms pour les types correspondants.
Donc isinstance()
devrait être préféré à type()
.
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
.
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
)