1475 votes

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

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

Utilisation de type() :

import types

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

Utilisation de isinstance() :

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

2 votes

Note : Si ce n'est pas str et unicode (où vous pouvez simplement vérifier basestring ), vous pouvez utiliser un tuple pour vérifier plusieurs types. Pour vérifier si something est int ou str utiliser isinstance(something, (int, str)) .

1 votes

type() retourne le type de l'objet que vous avez mis en argument, et n'est généralement pas utile à moins d'être comparé à un type réel (tel que type(9) == int ). isinstance() renvoie un booléen - vrai ou faux - selon que l'objet est du type donné. isinstance est généralement plus élégant à utiliser plutôt que d'écrire une vérification d'égalité encombrante, dans la plupart des cas.

1455voto

Alex Martelli Points 330805

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 un instance d'une classe de base, aussi), tout en vérifiant l'égalité de type ne le fait pas (elle exige l'identité des types et rejette les instances de sous-types, alias sous-classes).

Normalement, en Python, vous voulez que votre code supporte l'héritage, bien sûr (puisque l'héritage est si pratique, il serait mauvais d'empêcher le code qui utilise le vôtre de l'utiliser !), donc isinstance est moins mauvais que de vérifier l'identité de type car il prend en charge l'héritage de manière transparente.

Ce n'est pas que isinstance est bon l'esprit, c'est juste moins mauvais que de vérifier l'égalité des types. La solution normale, pythonique et préférée est presque invariablement le "duck typing" : essayez d'utiliser l'argument comme si qu'il soit d'un certain type souhaité, le faire dans une try / except capturant toutes les exceptions qui pourraient survenir si l'argument n'était pas en fait de ce type (ou de tout autre type l'imitant gentiment ;-), et dans l'instruction except essayez autre chose (en utilisant l'argument "comme si" il était d'un autre type).

basestring est Cependant, il s'agit d'un cas très particulier - un type intégré qui existe uniquement pour vous permettre d'utiliser isinstance (les deux str et unicode sous-classe basestring ). Les chaînes de caractères sont des séquences (vous pouvez boucler sur elles, les indexer, les découper, ...), mais vous voulez généralement les traiter comme des types "scalaires" - il est quelque peu incommode (mais c'est un cas d'utilisation raisonnablement fréquent) de traiter tous les types de chaînes de caractères (et peut-être d'autres types scalaires, c'est-à-dire ceux sur lesquels vous ne pouvez pas boucler) d'une certaine manière, tous les conteneurs (listes, ensembles, dicts, ...) d'une autre manière, et basestring plus isinstance vous aide à faire cela - la structure globale de cet 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 que basestring est un Classe de base abstraite ("ABC") - il n'offre aucune fonctionnalité concrète aux sous-classes, mais existe plutôt en tant que "marqueur", principalement pour être utilisé avec le module isinstance . Le concept est évidemment en plein essor dans 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, même si les ABC peuvent souvent se substituer à la dactylographie des canards, il n'y a généralement pas de grande pression pour le faire (voir ici ). Les ABC tels qu'ils sont implémentés dans les versions récentes de Python offrent cependant des avantages supplémentaires : isinstance (et issubclass ) peut maintenant signifier plus que simplement "[une instance de] une classe dérivée" (en particulier, toute classe peut être "enregistrée" avec un ABC de sorte qu'elle apparaîtra 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 d'une manière très naturelle via des applications du modèle de conception Template Method (voir ici et ici [[partie II]] pour plus de détails sur le DP TM, en général et spécifiquement en Python, indépendamment d'ABC).

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

Pour les besoins de cette réponse, l'élément clé à retenir au sujet des ABC (au-delà d'un placement sans doute plus naturel pour la fonctionnalité TM DP, par rapport à l'alternative classique de Python des classes mixines telles que UserDict.DictMixin ) est qu'ils font isinstance (et issubclass ) beaucoup plus attrayants et omniprésents (dans Python 2.6 et au-delà) qu'ils ne l'étaient auparavant (dans 2.5 et avant), et donc, par contraste, font de la vérification de l'égalité des types une pratique encore pire dans les versions récentes de Python qu'elle ne l'était déjà.

28 votes

Ce n'est pas que isinstance soit bon, remarquez, c'est juste moins mauvais que de vérifier l'égalité des types. La solution normale, pythonique et préférée est presque invariablement le "typage en canard". très de bons cas d'utilisation de isinstance() dans, disons, un interpréteur où les types reflètent la grammaire. Être "pythonicien" n'est pas tout !

2 votes

Basestring n'est pas disponible dans Python 3.

0 votes

@GeneCallahan, ce n'est pas parce qu'il existe de très bons cas que ce qui a été dit n'est pas une bonne règle générale. Je suis d'accord pour dire que la vérification du type à l'avance a certainement sa place, mais laisser les canards faire coin-coin devrait être une bonne chose. le plus de manière plus souple et plus efficace.

419voto

Peter Points 38320

Voici un exemple où isinstance réalise quelque chose qui type ne peut pas :

class Vehicle:
    pass

class Truck(Vehicle):
    pass

dans ce cas, l'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 ?

167 votes

Parce qu'il y a des cas où vous ne voulez pas du comportement isInstance, je dirais qu'il n'y a pas de "meilleur". Ils font simplement quelque chose de différent.

30 votes

-1, parce que "isinstance est meilleur que type" est un commentaire trompeur. il est compris comme " type est déprécié, utilisez isinstance à la place" à première vue. par exemple, ce que je voulais était exactement type() vérification, mais j'ai été induit en erreur pendant un court moment (et j'ai dû déboguer un peu) pour cette raison.

8 votes

C'est un bon exemple de leur différence de fonctionnement, mais je viens de rencontrer un cas où j'avais spécifiquement besoin de type() et non isinstance() . L'un n'est pas meilleur que l'autre ; ils sont destinés à des choses différentes.

122voto

Aaron Hall Points 7381

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

alors que le contrôle de type avec

type(obj) is Base

ne prend en charge que le type référencé.


En guise d'aparté, is est probablement plus approprié que

type(obj) == Base

parce que les classes sont des singletons.

Éviter la vérification de type - utiliser le polymorphisme (duck-typing)

En Python, vous voulez généralement autoriser n'importe quel type pour vos arguments, les traiter comme prévu, et si l'objet ne se comporte pas comme prévu, une erreur appropriée sera levée. C'est ce qu'on appelle le polymorphisme, également connu sous le nom de duck-typing.

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 d'autres choses qui sont des sous-types réels de canard :

function_of_duck(mallard)

ou qui travaillent comme un canard :

function_of_duck(object_that_quacks_and_swims_like_a_duck)

et notre code fonctionne toujours.

Cependant, il y a des cas où il est souhaitable de vérifier explicitement le type. Peut-être avez-vous des choses sensibles à faire avec différents types d'objets. Par exemple, l'objet Dataframe de Pandas peut être construit à partir de dicts ou les dossiers. Dans un tel cas, votre code doit savoir quel type d'argument il reçoit afin de pouvoir le traiter correctement.

Donc, pour répondre à la question :

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

Permettez-moi de vous démontrer la différence :

type

Disons que vous devez garantir un certain comportement si votre fonction reçoit 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 dans un dict qui est une sous-classe de dict (comme nous devrions pouvoir le faire, si nous attendons de notre code qu'il respecte le principe du Substitution de Liskov , que les sous-types peuvent être substitués aux types) notre code se 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 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')]))

renvoie à 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 divers types. Dans notre cas, si nous attendons seulement le Mapping 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 vérifier plusieurs classes à l'aide de la fonction 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 n'autorisez spécifiquement que ces types :

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

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

Mieux encore, inversez vos dépendances et ne vérifiez pas du tout les types spécifiques.

Conclusion

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

70voto

John Millikin Points 86775

Cette dernière solution est préférable, car elle permet de gérer correctement les sous-classes. En fait, votre exemple peut être écrit encore plus facilement car isinstance() Le second paramètre de la commande peut être un tuple :

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

ou, en utilisant le basestring classe abstraite :

if isinstance(b, basestring):
    do_something_else()

19voto

Alec A Points 4400

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

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

isinstance(True, int)

et

isinstance(False, int)

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

type(True) == int

renvoie à False .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X