Quel est le meilleur (idiomatiques) façon de vérifier le type d'une variable Python? [dupliquer]
-
22-08-2019 - |
Question
Cette question a déjà une réponse ici:
Je dois savoir si une variable en Python est une chaîne ou un dict. Y at-il quelque chose de mal avec le code suivant?
if type(x) == type(str()):
do_something_with_a_string(x)
elif type(x) == type(dict()):
do_somethting_with_a_dict(x)
else:
raise ValueError
Mise à jour :. J'ai accepté la réponse de avisser (bien que je vais changer d'avis si quelqu'un explique pourquoi isinstance
est préféré au type(x) is
)
Mais grâce à nakedfanatic pour me rappeler qu'il est souvent plus propre d'utiliser un dict (comme une déclaration de cas) que si un / Elif / série autre.
Permettez-moi de préciser mon cas d'utilisation. Si une variable est une chaîne, je dois le mettre dans une liste. Si c'est un dict, je besoin d'une liste des valeurs uniques. Voici ce que je suis venu avec:
def value_list(x):
cases = {str: lambda t: [t],
dict: lambda t: list(set(t.values()))}
try:
return cases[type(x)](x)
except KeyError:
return None
Si isinstance
est préféré, comment voulez-vous écrire cette fonction value_list()
?
La solution
Qu'est-ce qui se passe si quelqu'un passe une chaîne unicode à votre fonction? Ou une classe dérivée de dict? Ou une classe qui implémente une interface comme dict? code suivant couvre deux premiers cas. Si vous utilisez Python 2.6, vous pouvez utiliser collections.Mapping
au lieu de dict
selon la ABC PEP .
def value_list(x):
if isinstance(x, dict):
return list(set(x.values()))
elif isinstance(x, basestring):
return [x]
else:
return None
Autres conseils
type(dict())
dit « faire une nouvelle dict, puis savoir ce que son type est ». Il est plus rapide de dire que « dict ».
Mais si vous voulez juste vérifier le type, d'une manière plus idiomatiques est isinstance(x, dict)
.
Notez que isinstance
comprend également les sous-classes (merci Dustin ):
class D(dict):
pass
d = D()
print("type(d) is dict", type(d) is dict) # -> False
print("isinstance (d, dict)", isinstance(d, dict)) # -> True
types intégrés en Python ont construit dans les noms:
>>> s = "hallo"
>>> type(s) is str
True
>>> s = {}
>>> type(s) is dict
True
Notez BTW est opérateur. Cependant, la vérification de type (si vous voulez l'appeler ainsi) est généralement fait en enroulant un test spécifique de type dans un essai, sauf clause, car il est pas tant le type de la variable qui est important, mais si vous pouvez faire un certain quelque chose avec elle ou non.
isinstance est preferrable sur le type, car il évalue également vrai lorsque vous comparez une instance d'objet avec ses superclasse, ce qui signifie essentiellement que vous ne jamais avoir à cas particulier votre ancien code pour l'utiliser avec dict ou sous-str.
Par exemple:
>>> class a_dict(dict):
... pass
...
>>> type(a_dict()) == type(dict())
False
>>> isinstance(a_dict(), dict)
True
>>>
Bien sûr, il pourrait y avoir des situations où vous ne voudriez pas ce comportement, mais ce sont -hopefully- beaucoup moins fréquents que les cas où vous ne le voulez.
Je pense que je vais aller à l'approche de frappe de canard - « si ça marche comme un canard, il caquette comme un canard, son canard ». De cette façon, vous devez vous inquiétez pas si la chaîne est unicode ou ascii.
Voici ce que je ferai:
In [53]: s='somestring'
In [54]: u=u'someunicodestring'
In [55]: d={}
In [56]: for each in s,u,d:
if hasattr(each, 'keys'):
print list(set(each.values()))
elif hasattr(each, 'lower'):
print [each]
else:
print "error"
....:
....:
['somestring']
[u'someunicodestring']
[]
Les experts ici sont invités à commenter ce type d'utilisation de duck typing, je l'ai utilisé mais je me suis présenté au concept exact derrière elle ces derniers temps et je suis très excité à ce sujet. Je voudrais donc savoir si c'est un surpuissant à faire.
Je pense qu'il pourrait être préférable de le faire réellement
if isinstance(x, str):
do_something_with_a_string(x)
elif isinstance(x, dict):
do_somethting_with_a_dict(x)
else:
raise ValueError
2 formes alternatives, en fonction de votre code un ou l'autre est probablement considéré comme mieux que cela même. L'un est de ne pas regarder avant de sauter
try:
one, two = tupleOrValue
except TypeError:
one = tupleOrValue
two = None
L'autre approche est de Guido et est une forme de surcharge de fonction qui laisse votre code plus ouvert terminé.
Cela devrait fonctionner - donc non, il n'y a rien de mal avec votre code. Cependant, il pourrait également être fait avec un dict:
{type(str()): do_something_with_a_string,
type(dict()): do_something_with_a_dict}.get(type(x), errorhandler)()
Un peu plus concis et pythonique ne dites-vous?
Modifier .. conseils de Avisser Ne écoutant, le code fonctionne aussi comme ça, et semble plus agréable:
{str: do_something_with_a_string,
dict: do_something_with_a_dict}.get(type(x), errorhandler)()
Vous pouvez consulter typecheck. http://pypi.python.org/pypi/typecheck
module de vérification de type pour Python
Ce paquet fournit de puissantes installations de contrôle de type temps d'exécution pour les fonctions Python, des méthodes et des générateurs. Sans exiger un préprocesseur personnalisé ou une modification de la langue, le paquet typecheck permet aux programmeurs et ingénieurs d'assurance qualité pour faire des affirmations précises sur l'entrée et la sortie de leur code.
Je l'ai utilisé une approche différente:
from inspect import getmro
if (type([]) in getmro(obj.__class__)):
# This is a list, or a subclass of...
elif (type{}) in getmro(obj.__class__)):
# This one is a dict, or ...
Je ne me souviens pas pourquoi j'utilisé ce lieu de isinstance, mais ...
*sigh*
Non, les arguments en python est contrôle de type pas nécessaire. Il est jamais nécessaire.
Si votre code accepte une chaîne ou un objet dict, votre conception est cassé.
Cela vient du fait que si vous ne connaissez pas encore le type d'un objet dans votre propre programme, alors vous faites quelque chose de mal déjà.
TypeChecking mal réutilisation du code et réduit les performances. Avoir une fonction effectue des choses différentes en fonction du type de l'objet est passé sujettes aux bugs et a un comportement plus difficile à comprendre et à maintenir.
Vous avez les options suivantes plus raisonnables:
1) Faire une unique_values
fonction qui convertit dicts dans les listes de valeurs uniques:
def unique_values(some_dict):
return list(set(some_dict.values()))
Faites votre fonction suppose l'argument passé est toujours une liste. De cette façon, si vous devez passer une chaîne à la fonction, vous faites juste:
myfunction([some_string])
Si vous devez passer un dict, vous faites:
myfunction(unique_values(some_dict))
C'est votre meilleure option, il est propre, facile à comprendre et à entretenir. N'importe qui la lecture du code comprend immediatelly ce qui se passe, et vous n'avez pas à typer.
2) Faire deux fonctions, l'une qui accepte des listes de chaînes et qui accepte
dicts. Vous pouvez faire un appel l'autre interne, dans le plus pratique
manière (myfunction_dict
peut créer une liste de chaînes et d'appeler myfunction_list
).
Dans tous les cas, ne typecheck pas . Il est tout à fait inutile et a seulement inconvénients. Factoriser votre code plutôt que d'une manière que vous ne avez pas besoin de typer. Vous obtenez seulement des avantages à le faire, à la fois à court et à long terme.