Quelle est la manière canonique de vérifier le type en Python?


1278

Quelle est la meilleure façon de vérifier si un objet donné est d'un type donné? Que diriez-vous de vérifier si l'objet hérite d'un type donné?

Disons que j'ai un objet o. Comment puis-je vérifier s'il s'agit d'un str?


7
Eh bien, l'approche canonique en Python est de ne pas vérifier du tout le type (sauf si vous déboguez). Habituellement, vous essayez simplement de l'utiliser comme une chaîne (par exemple, concaténer avec d'autres chaînes, imprimer sur la console, etc.); si vous pensez que cela pourrait échouer, utilisez try / except ou hasattr. Cela dit, la réponse acceptée est la manière canonique de faire ce que vous ne devriez généralement pas faire dans le monde Python. Pour plus d'informations, google "Python duck typing" ou lisez ceci: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs

9
Je pense que M. Coombs néglige des exemples comme les classes sérialisables non JSON. Si vous mettez un gros morceau de données à travers une fonction (dont on ne peut pas influencer le code), vous voudrez peut-être convertir certains morceaux de ces données en, par exemple, un <str> avant de le passer. C'est du moins ainsi que je me suis retrouvé sur cette page ...
John Carrell

2
Il semble que la raison la plus courante pour demander cela est que l'on veut faire la distinction entre les chaînes et les itérables des chaînes. C'est une question délicate car les chaînes sont itérables de chaînes - une chaîne à un seul caractère est même une séquence d'elle-même (la dernière fois que j'ai vérifié - on ne devrait probablement pas s'y fier). Mais quelqu'un aurait-il jamais pu utiliser quelque chose comme une chaîne? Oui . Donc, la réponse à "Que dois-je faire pour faire la distinction entre les chaînes et les autres itérables des chaînes?" est correctement: "Cela dépend de ce que vous essayez de faire". :-D
clacke

2
Les annotations de type Python sont désormais chose du tout. Jetez un oeil à mypy
Sheena

Réponses:


1522

Pour vérifier s'il os'agit d'une instance strou d'une sous-classe de str, utilisez isinstance (ce serait la méthode "canonique"):

if isinstance(o, str):

Pour vérifier si le type de oest exactement str(exclure les sous-classes):

if type(o) is str:

Les éléments suivants fonctionnent également et peuvent être utiles dans certains cas:

if issubclass(type(o), str):

Voir Fonctions intégrées dans la référence de la bibliothèque Python pour des informations pertinentes.

Une dernière remarque: dans ce cas, si vous utilisez Python 2, vous voudrez peut-être utiliser:

if isinstance(o, basestring):

car cela interceptera également les chaînes Unicode ( unicoden'est pas une sous-classe de str; les deux stret unicodesont des sous-classes de basestring). Notez qu'il basestringn'existe plus en Python 3, où il y a une séparation stricte des chaînes ( str) et des données binaires ( bytes).

Alternativement, isinstanceaccepte un tuple de classes. Cela retournera Truesi oest une instance d'une sous-classe de l'un des (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ subclasses __ () ne renvoie que les sous-classes directes de str et ne fait pas la même chose que issubclass () ou isinstance (). (Pour ce faire, vous devez appeler récursivement .__ sous-classes __ ().
Thomas Wouters

15
C'est une bonne réponse, mais je pense que cela devrait vraiment commencer par un avertissement que vous ne devriez généralement pas faire cela en Python. En l'état, il semble valider l'hypothèse qu'il s'agit d'une "chose canonique à faire en Python", ce qui n'est pas le cas.
Jon Coombs

4
Ce sont des réponses python2. Par exemple, il n'y a pas de chaîne de base en python3.
dfrankow

4
Quelle est la différence entre l'instance et "exactement"? Si type(a) is Objectalors n'est-ce pas également vrai isinstance(a, Object). Cependant, si type(a) is SubClassOfObject, alors type(a) is Object == False, mais isinstance(a, Object) == True. Droite?
mavavilj

1
@mavavilj - a is bsignifie que a et b sont exactement la même chose, c'est-à-dire des références à la même entité en mémoire. Donc, aet bdevrait être exactement la même classe, pas des sous-classes, comme avec isinstance(). Voir par exemple stackoverflow.com/a/133024/1072212
Terry Brown

196

La façon la plus pythonique de vérifier le type d'un objet est ... de ne pas le vérifier.

Étant donné que Python encourage Duck Typing , vous devez simplement try...exceptutiliser les méthodes de l'objet comme vous le souhaitez. Donc, si votre fonction recherche un objet fichier inscriptible, ne vérifiez pas qu'il s'agit d'une sous-classe de file, essayez simplement d'utiliser sa .write()méthode!

Bien sûr, parfois ces belles abstractions se décomposent et isinstance(obj, cls)c'est ce dont vous avez besoin. Mais utilisez avec parcimonie.


75
À mon humble avis, la façon la plus pythonique est de faire face à tout argument qui est donné. Dans mon code, je ne peux souvent pas savoir si je reçois un objet ou un tableau d'objets, et j'utilise la vérification de type en interne pour convertir un seul objet en une liste à un élément.
sastanin

14
Plutôt que d'essayer simplement d'utiliser sa méthode d'écriture, il y a des moments où vous voulez le faire sans provoquer d'exception. Dans ce cas, vous pouvez faire ... if hasattr(ob, "write") and callable(ob.write): Ou enregistrer un accès dict ...func = getattr(ob, "write", None) if callable(func): ...
ideasman42

142
La saisie de canard consiste à utiliser une bibliothèque. La vérification de type consiste à écrire une bibliothèque. Pas le même domaine problématique.
RickyA

16
@RickyA, je ne suis pas d'accord. Le typage canard consiste à interagir avec des objets en utilisant des interfaces avec une sémantique bien connue. Cela peut s'appliquer au code de bibliothèque ou au code qui utilise une telle bibliothèque.
Dan Lenski

6
@ nyuszika7h, En Python3 hasattrne supprime qu'une AttributeError - Voir: docs.python.org/3.4/library/functions.html#hasattr
ideasman42

57

isinstance(o, str)retournera Truesi oest un strou est d'un type qui hérite de str.

type(o) is strretournera Truesi et seulement si oest une chaîne. Il retournera Falsesi oest d'un type qui hérite de str.


6
Bien sûr, cela échouera si l'objet n'est pas une instance de 'str', mais plutôt quelque chose de semblable à une chaîne. Comme unicode, mmap, UserString ou tout autre type défini par l'utilisateur. L'approche habituelle en Python n'est pas de faire des vérifications de type.
Thomas Wouters

6
Vous n'avez pas à vous excuser, c'est OK pour répondre à votre propre question. SO est pour les réponses, pas pour le karma.
Eli Bendersky

2
C'est très utile. Parce que la différence entre isinstanceet type(var) == type('')n'est pas claire.
sastanin

30

Après que la question a été posée et répondue, des indices de type ont été ajoutés à Python . Les indices de type en Python permettent de vérifier les types mais d'une manière très différente des langages typés statiquement. Les indices de type en Python associent les types d'arguments attendus aux fonctions en tant que données accessibles à l'exécution associées aux fonctions, ce qui permet de vérifier les types. Exemple de syntaxe d'indication de type:

def foo(i: int):
    return i

foo(5)
foo('oops')

Dans ce cas, nous voulons qu'une erreur soit déclenchée foo('oops')car le type annoté de l'argument est int. L'indicateur de type ajouté ne provoque pas d'erreur lors de l'exécution normale du script. Cependant, il ajoute des attributs à la fonction décrivant les types attendus que d'autres programmes peuvent interroger et utiliser pour vérifier les erreurs de type.

L'un de ces autres programmes qui peuvent être utilisés pour rechercher l'erreur de type est mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Vous devrez peut-être installer à mypypartir de votre gestionnaire de paquets. Je ne pense pas qu'il soit livré avec CPython mais semble avoir un certain niveau d '"officialité".)

La vérification de type de cette manière est différente de la vérification de type dans les langages compilés de type statique. Étant donné que les types sont dynamiques en Python, la vérification de type doit être effectuée au moment de l'exécution, ce qui impose un coût - même sur les programmes corrects - si nous insistons pour que cela se produise à chaque chance. Les vérifications de type explicites peuvent également être plus restrictives que nécessaire et provoquer des erreurs inutiles (par exemple, l'argument doit-il vraiment être exactement de listtype ou quelque chose d'itérable est-il suffisant?).

L'avantage de la vérification de type explicite est qu'il peut détecter les erreurs plus tôt et donner des messages d'erreur plus clairs que la frappe de canard. Les exigences exactes d'un type de canard ne peuvent être exprimées qu'avec une documentation externe (espérons-le, elle est complète et précise) et des erreurs de types incompatibles peuvent se produire loin de leur origine.

Les conseils de type de Python sont censés offrir un compromis où les types peuvent être spécifiés et vérifiés mais il n'y a aucun coût supplémentaire lors de l'exécution de code habituelle.

Le typingpackage propose des variables de type qui peuvent être utilisées dans des conseils de type pour exprimer les comportements nécessaires sans nécessiter de types particuliers. Par exemple, il inclut des variables telles que Iterableet Callablepour des conseils pour spécifier la nécessité de tout type avec ces comportements.

Bien que les indices de type soient la façon la plus Pythonique de vérifier les types, il est souvent encore plus Pythonic de ne pas vérifier les types du tout et de s'appuyer sur la frappe de canard. Les indices de type sont relativement nouveaux et le jury n'est toujours pas sur le moment où ils sont la solution la plus Pythonic. Une comparaison relativement peu controversée mais très générale: les astuces de type fournissent une forme de documentation qui peut être appliquée, permettre au code de générer des erreurs plus tôt et plus faciles à comprendre, peut détecter des erreurs que la frappe de canard ne peut pas, et peut être vérifiée statiquement (dans un cas inhabituel sens mais il est toujours en dehors de l'exécution). D'un autre côté, le typage canard est la méthode Pythonique depuis longtemps, n'impose pas le surcoût cognitif du typage statique, est moins verbeux et accepte tous les types viables, puis certains.


2
-1: mypy s'appelle spécifiquement un "vérificateur de type statique" donc je ne suis pas sûr d'où vous avez obtenu "la vérification de type doit être effectuée au moment de l'exécution".
Kevin

@Kevin Rétrospectivement, c'était une digression inutile, mais pour aller plus loin, les indications de type de Python sont transformées en données d'exécution et mypyest un module Python qui utilise importlibpour accéder à ces données. Que ce soit une "vérification de type statique" est une question philosophique mais elle est différente de ce que la plupart des gens pourraient attendre puisque l'interpréteur de langage normal et le mécanisme d'importation sont impliqués.
Praxeolitic

4
Ce n'est pas vrai non plus. Il utilise typed_ast, qui n'est lui -même qu'un clone de ast avec des fonctionnalités supplémentaires. ast n’importe pas de modules; il les analyse dans un arbre de syntaxe abstraite.
Kevin

18

Voici un exemple pour lequel la frappe de canard est mauvaise sans savoir quand elle est dangereuse. Par exemple: Voici le code Python (en omettant éventuellement une indentation appropriée), notez que cette situation est évitable en prenant soin des fonctions isinstance et issubclassof pour vous assurer que lorsque vous avez vraiment besoin d'un canard, vous n'obtenez pas de bombe.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Même avec la vérification de type, vous pouvez créer une class EvilDuck(Duck)conversation et la remplacer (). Ou plus probablement, class ChineseCancerDuck(Duck)avec un effet secondaire désagréable qui n'apparaît que des années plus tard. Vous feriez mieux de superviser votre enfant (et de tester ses jouets à fond :)
Brett Thomas

36
Les bombes ne parlent pas. N'ajoutez pas de méthodes absurdes et cela ne se produira pas.
droite

7
@Dmitry, c'est la critique courante de Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... vous dites essentiellement que toute interface pour laquelle la sémantique n'est pas appliquée par la langue est mauvaise. Je crois que c'est plus l'approche de Java. L'intérêt du typage de canard de Python est qu'il ne fonctionne que lorsqu'il existe une convention généralement reconnue sur la signification des interfaces spécifiques. Par exemple, vous pouvez utiliser beaucoup de code Python en remplaçant l' __file__attribut (couramment utilisé pour identifier les objets de type fichier) pour signifier autre chose.
Dan Lenski

2
Tout cela revient à la vieille blague "Docteur, ça fait mal quand je fais ça." ... "Alors ne fais pas ça.". Insatisfaisant pour quelqu'un qui est habitué à "s'il compile, il s'exécute", mais c'est pourquoi tester l'obsession est né du monde du langage dynamique.
clacke

1
@clacke fondamentalement, il est trop coûteux d'appliquer des types au moment de l'exécution strictement parce que TOUT doit être un objet (afin de mapper de la chaîne à n'importe quel type possible), et trop pratique pour ne pas avoir ducktyping car le ducktyping permet des techniques de prototypage vraiment puissantes qui surmontent les choses qui sont normalement très difficiles à faire avec des interfaces rigides. En outre, tout langage statique est confronté à un point où il doit créer un typage de canard via des bibliothèques dynamiques, une évaluation et une stringification ou des interfaces, et ces choses ne le rendent pas intrinsèquement mauvais, juste très puissant.
Dmitry

12
isinstance(o, str)

Lien vers les documents


1
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change.
EKons

7

Je pense que la bonne chose à propos de l'utilisation d'un langage dynamique comme Python est que vous ne devriez vraiment pas avoir à vérifier quelque chose comme ça.

Je voudrais simplement appeler les méthodes requises sur votre objet et attraper un AttributeError. Plus tard, cela vous permettra d'appeler vos méthodes avec d'autres objets (apparemment sans rapport) pour accomplir différentes tâches, telles que se moquer d'un objet à tester.

J'ai beaucoup utilisé cela pour obtenir des données du Web avec urllib2.urlopen()lesquelles renvoie un fichier comme un objet. Cela peut à son tour être transmis à presque n'importe quelle méthode qui lit à partir d'un fichier, car il implémente la même read()méthode qu'un fichier réel.

Mais je suis sûr qu'il y a un temps et un lieu d'utilisation isinstance(), sinon il ne serait probablement pas là :)


Un bon exemple de quand vous devez l' utiliser est si vous analysez un objet json dynamique. Vous ne savez pas à l'avance si un champ est une chaîne ou un dictionnaire.
Gray

6

Pour les validations de types plus complexes, j'aime l'approche de typeguard de la validation basée sur les annotations d' indices de type python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Vous pouvez effectuer des validations très complexes de manière très propre et lisible.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Vous pouvez vérifier le type d'une variable en utilisant __name__ d'un type.

Ex:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Merci, c'est le code secret que je voulais quand je l'ai affiché en tant que feedback à l'utilisateur. Ça m'a pris trop de temps pour trouver ça ...
Aaron D. Marasco

5

À Hugo:

Vous voulez probablement dire listplutôt que array, mais cela pointe tout le problème de la vérification de type - vous ne voulez pas savoir si l'objet en question est une liste, vous voulez savoir s'il s'agit d'une sorte de séquence ou s'il s'agit d'un seul objet. Essayez donc de l'utiliser comme une séquence.

Supposons que vous souhaitiez ajouter l'objet à une séquence existante, ou s'il s'agit d'une séquence d'objets, ajoutez-les tous

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Une astuce avec cela est si vous travaillez avec des chaînes et / ou des séquences de chaînes - c'est délicat, car une chaîne est souvent considérée comme un seul objet, mais c'est aussi une séquence de caractères. Pire que cela, car c'est vraiment une séquence de cordes de longueur unique.

Je choisis généralement de concevoir mon API de sorte qu'elle n'accepte qu'une seule valeur ou une séquence - cela facilite les choses. Il n'est pas difficile de mettre un [ ]autour de votre valeur unique lorsque vous la transmettez si nécessaire.

(Bien que cela puisse provoquer des erreurs avec les chaînes, car elles ressemblent à des séquences.)


0

Un moyen simple de vérifier le type est de le comparer avec quelque chose dont vous connaissez le type.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True


-7

Vous pouvez vérifier avec la ligne ci-dessous pour vérifier de quel type de caractère la valeur donnée est:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Vous êtes sûr de ne pas vouloir supprimer cette réponse @Venkatesan?
Gray
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.