J'ai besoin de savoir si une variable dans 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 d'avisser (bien que je changerai d'avis si quelqu'un explique pourquoi isinstance
est préférable à type(x) is
).
Mais merci à nakedfanatic de m'avoir rappelé qu'il est souvent plus simple d'utiliser un dict (en tant qu'affirmation de cas) qu'une série if/Elif/else.
Laissez-moi élaborer sur mon cas d'utilisation. Si une variable est une chaîne, je dois la mettre dans une liste. Si c'est un dict, j'ai 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 vous préférez isinstance
, comment écririez-vous cette fonction value_list()
?
Que se passe-t-il si quelqu'un passe une chaîne Unicode à votre fonction? Ou une classe dérivée de dict? Ou une classe implémentant une interface de type dict? Le code suivant couvre les deux premiers cas. Si vous utilisez Python 2.6, vous pouvez utiliser collections.Mapping
au lieu de dict
selon le ABC PEP .
_def value_list(x):
if isinstance(x, dict):
return list(set(x.values()))
Elif isinstance(x, basestring):
return [x]
else:
return None
_
type(dict())
dit "créez un nouveau dict, puis déterminez quel est son type". C'est plus rapide de dire juste "dict". Mais si vous voulez juste vérifier le type, une manière plus idiomatique est isinstance(x, dict)
.
Notez que isinstance
inclut également des 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
les types intégrés dans Python ont des noms intégrés:
>>> s = "hallo"
>>> type(s) is str
True
>>> s = {}
>>> type(s) is dict
True
remarquez l'opérateur est. Cependant, la vérification du type (si vous voulez l'appeler ainsi) est généralement effectuée en encapsulant un test spécifique au type dans une clause try-except, car ce n'est pas vraiment le type de la variable qui est important, mais la possibilité de faire un certain test. quelque chose avec ou pas.
isinstance est préférable au type car elle est également évaluée comme True lorsque vous comparez une instance d'objet à une super-classe, ce qui signifie que vous n'aurez jamais à mettre votre ancien code dans un cas spécial pour l'utiliser avec des sous-classes dict ou str.
Par exemple:
>>> class a_dict(dict):
... pass
...
>>> type(a_dict()) == type(dict())
False
>>> isinstance(a_dict(), dict)
True
>>>
Bien sûr, il peut arriver que vous ne souhaitiez pas ce comportement dans certaines situations, mais elles sont, espérons-le, beaucoup moins courantes que celles dans lesquelles vous le souhaitez.
Je pense que je vais opter pour la méthode de frappe au canard: "s’il marche comme un canard, il charrie comme un canard, c’est un canard". De cette façon, vous n'aurez plus à vous soucier de savoir si la chaîne est unicode ou ascii.
Voici ce que je vais faire:
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 présents sont invités à commenter ce type d’utilisation du duktkting. Je l’utilise, mais je me suis familiarisé avec le concept exact qui le sous-tend récemment et je suis très enthousiaste à ce sujet. Je voudrais donc savoir s’il ya trop de travail à faire.
Je pense qu'il pourrait être préférable de faire
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, selon votre code l'un ou l'autre est probablement considéré comme meilleur que cela même. On est de ne pas regarder avant de sauter
try:
one, two = tupleOrValue
except TypeError:
one = tupleOrValue
two = None
L'autre approche est celle de Guido et consiste en une surcharge de fonctions qui laisse votre code plus ouvert.
Vous voudrez peut-être vérifier typecheck. http://pypi.python.org/pypi/typecheck
Module de vérification de type pour Python
Ce paquet fournit de puissantes fonctionnalités de vérification typographique au moment de l’exécution pour les fonctions, méthodes et générateurs Python. Sans nécessiter de préprocesseur personnalisé ni de modification de la langue, le package typecheck permet aux programmeurs et aux ingénieurs d'assurance qualité de formuler des affirmations précises sur les entrées et les sorties de code.
Cela devrait fonctionner - alors non, il n'y a rien de mal avec votre code. Cependant, cela pourrait aussi ê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 Pythonic ne dirais-tu pas?
Edit .. En tenant compte des conseils d'Avisser, le code fonctionne également comme ceci et est plus joli:
{str: do_something_with_a_string,
dict: do_something_with_a_dict}.get(type(x), errorhandler)()
J'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'ai utilisé cela à la place d'isinstance, bien que ...
*sigh*
Non, la vérification des arguments dans python n'est pas nécessaire. C'est jamais nécessaire.
Si votre code accepte une chaîne ou un objet dict, votre conception est endommagée.
Cela vient du fait que si vous ne connaissez pas déjà le type d'objet dans votre propre programme, vous faites déjà quelque chose de mal.
Le typececking nuit à la réutilisation du code et réduit les performances. Avoir une fonction qui effectue différentes choses selon le type d'objet transmis est sujet aux bogues et a un comportement plus difficile à comprendre et à maintenir.
Vous avez les options d'assainissement suivantes:
1) Créez une fonction unique_values
qui convertit les dessins en listes de valeurs uniques:
def unique_values(some_dict):
return list(set(some_dict.values()))
Faites que votre fonction suppose que l'argument passé est toujours une liste. De cette façon, si vous devez passer une chaîne à la fonction, il vous suffit de faire:
myfunction([some_string])
Si vous avez besoin de passer un dict, vous faites:
myfunction(unique_values(some_dict))
C’est votre meilleure option, c’est propre, facile à comprendre et à entretenir. Toute personne lisant immédiatement le code comprend ce qui se passe et vous n'avez pas à vérifier votre code.
2) Créez deux fonctions, une qui accepte les listes de chaînes et une qui accepte les dict. Vous pouvez appeler les uns les autres en interne, de la manière la plus pratique (myfunction_dict
peut créer une liste de chaînes et appeler myfunction_list
).
Dans tous les cas, ne pas taper sur le clavier. C'est complètement inutile et n'a que des inconvénients. Refacturez votre code à la place d'une manière que vous n'avez pas besoin de dactylographier. Vous n'obtenez que des avantages, à court et à long terme.