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




checking default (5)

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

Vérification de type avec

isinstance(obj, Base)

permet des instances de sous-classes et plusieurs bases possibles:

isinstance(obj, (Base1, Base2))

alors que la vérification de type avec

type(obj) is Base

prend uniquement en charge le type référencé.

En tant que sidenote, est probablement plus approprié que

type(obj) == Base

parce que les classes sont des singletons.

Eviter la vérification de type - utiliser Polymorphisme (dactylographie)

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 déclenchera une erreur appropriée. C'est ce qu'on appelle le polymorphisme, également connu sous le nom de dactylographie.

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

Si le code ci-dessus fonctionne, on peut supposer que notre argument est un canard. Ainsi, nous pouvons passer à d'autres choses sont des sous-types réels de canard:

function_of_duck(mallard)

ou ça marche 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 sensées à faire avec différents types d'objets. Par exemple, l'objet Pandas Dataframe peut être construit à partir de dicts ou d' enregistrements. Dans un tel cas, votre code doit savoir quel type d'argument il obtient afin qu'il puisse le gérer correctement.

Donc, pour répondre à la question:

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

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

type

Supposons que vous ayez besoin d'un certain comportement si votre fonction obtient un certain type d'argument (un cas d'utilisation courant pour les constructeurs). Si vous vérifiez pour le type comme ceci:

def foo(data):
    '''accepts a dict to construct something, string support in future'''
    if type(data) is 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 attendons que notre code suive le principe de la substitution de Liskov , que les sous-types puissent être substitués aux types) nos codes break!

from collections import OrderedDict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

déclenche 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 différents types. Dans notre cas, si nous attendons seulement le protocole 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

Conclusion

Donc, comme nous voulons prendre en charge les sous-classes substituantes, 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 , sauf si vous avez vraiment besoin de connaître la classe précise d'une instance.

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

Ce dernier est préféré, car il traitera correctement les sous-classes. En fait, votre exemple peut être écrit encore plus facilement parce isinstance() le deuxième 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()

Voici pourquoi isinstance vaut mieux que type :

class Vehicle:
    pass

class Truck(Vehicle):
    pass

dans ce cas, un objet de 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?


Pour résumer le contenu d'autres réponses (déjà bonnes!), isinstance s'adresse à l'héritage (une instance d'une classe dérivée est aussi une instance d'une classe de base), tout en vérifiant l'égalité de type (elle 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 très pratique, il serait mauvais d'arrêter l'utilisation du code!), isinstance est moins mauvais que de vérifier l'identité du type s parce qu'il prend en charge de façon transparente l'héritage.

Ce n'est pas que l' isinstance soit bonne , attention, c'est juste moins mauvais que de vérifier l'égalité des types. La solution normale, Pythonic, préférée est presque invariablement "duck typing": essayez d'utiliser l'argument comme s'il s'agissait d'un certain type désiré, faites-le dans une instruction try / except interceptant toutes les exceptions qui pourraient survenir si l'argument n'était pas de ce type (ou de tout autre type qui imite joliment ;-), et dans la clause except , essayez quelque chose d'autre (en utilisant l'argument "comme si" c'était un autre type).

basestring est , cependant, un cas tout à fait particulier - un type intégré qui n'existe que pour vous permettre d'utiliser isinstance ( isinstance sous-classe str et unicode ). Les chaînes sont des séquences (vous pouvez les faire défiler, les indexer, les découper, ...), mais vous voulez généralement les traiter comme des types "scalaires" - c'est quelque peu inconcevable (mais un cas d'utilisation raisonnablement fréquent) de traiter toutes sortes de d'une autre manière, tous les conteneurs (listes, ensembles, dicts, ...) d'une autre manière, et isinstance plus isinstance vous aide à le faire - 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 une classe de base abstraite ("ABC") - elle n'offre aucune fonctionnalité concrète aux sous-classes, mais existe plutôt comme un "marqueur", principalement pour une utilisation avec isinstance . Le concept est évidemment croissant en Python, puisque le 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, bien que l'ABC puisse souvent remplacer le typage du canard, il n'y a généralement pas de grande pression pour le faire (voir here ). Les ABCs implémentés dans les versions récentes de Python offrent cependant des bonus supplémentaires: isinstance (et issubclass ) peut désormais signifier plus que simplement "[une instance de] une classe dérivée" (en particulier, n'importe quelle classe peut être "enregistrée" avec un ABC il apparaîtra comme une sous-classe, et ses instances comme des instances de l'ABC); et ABCs peut également offrir une commodité supplémentaire aux sous-classes réelles d'une manière très naturelle via les applications de modèle de conception de méthode de template (voir here et here [[partie II]] pour plus sur le TM DP, en général et spécifiquement en Python, indépendant de ABC) .

Pour la mécanique sous-jacente du support ABC telle que proposée dans Python 2.6, voir here ; pour leur version 3.1, très similaire, voir here . Dans les deux versions, les collections modules de bibliothèque standard (c'est la version 3.1 - pour la version 2.6 très similaire, voir here ) offre plusieurs ABC utiles.

Dans le but de cette réponse, la clé pour retenir ABC (au-delà d'un placement plus naturel pour la fonctionnalité TM DP, comparé à l'alternative classique Python des classes mixin comme UserDict.DictMixin ) est qu'ils font isinstance (et issubclass ) beaucoup plus attrayant et envahissant (en Python 2.6 et en avant) qu'auparavant (en 2.5 et avant), et par conséquent, faire de l'égalité des types de vérification une pratique encore pire dans les versions récentes de Python qu'elle ne l'était déjà.


C'est assez simple en fait.

Vous pouvez considérer une interface comme une classe qui ne peut avoir que des méthodes abstraites et rien d'autre.

Ainsi, une interface peut seulement "déclarer" et ne pas définir le comportement que vous voulez que la classe ait.

Une classe abstraite vous permet à la fois de déclarer (en utilisant des méthodes abstraites) et de définir (en utilisant des implémentations de méthodes complètes) le comportement que vous voulez que la classe ait.

Et une classe régulière ne vous permet que de définir, et non de déclarer, le comportement / les actions que vous voulez que la classe ait.

Une dernière chose,

En Java, vous pouvez implémenter plusieurs interfaces, mais vous ne pouvez en étendre qu'une (classe ou classe abstraite) ...

Cela signifie que l'héritage du comportement défini est limité à un seul par classe ... Par exemple, si vous voulez une classe qui encapsule le comportement des classes A, B et C, vous devez effectuer les opérations suivantes: Classe A étend B, Classe C étend A C'est un peu un tour d'avoir un héritage multiple ...

Interfaces d'autre part, vous pouvez simplement faire: l'interface C implémente A, B

Donc, en effet, Java ne prend en charge l'héritage multiple que dans le "comportement déclaré", c'est-à-dire les interfaces, et seulement l'héritage unique avec un comportement défini .. à moins que vous ne fassiez le tour comme je l'ai décrit ...

J'espère que cela a du sens.





python oop inheritance types