Comment vérifier si une variable est un dictionnaire en Python?

214

Comment vérifieriez-vous si une variable est un dictionnaire en python?

Par exemple, j'aimerais qu'il parcourt les valeurs du dictionnaire jusqu'à ce qu'il trouve un dictionnaire. Ensuite, parcourez celui qu'il trouve:

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for k, v in dict.iteritems():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)
Riley
la source
Aussi stackoverflow.com/questions/378927/… (qui est marqué comme un double de celui ci-dessus).
NPE
40
Non, ce n'est pas la même question. La réponse à cette question et aux autres questions énumérées ici contient toutes sensiblement les mêmes informations. Mais la réponse à "Comment vérifier si une variable est un dictionnaire en python" est "Utilisez type () ou isinstance ()", ce qui conduit à une nouvelle question, quelle est la différence entre type () et isinstance () . Mais la personne qui pose la première question ne peut pas le savoir tant que la première question n'a pas été répondue. Ergo, différentes questions, ce qui compte lorsque vous recherchez votre question sur le site.
13
Je suis d'accord avec @mbakeranalecta Je suis venu ici à la recherche de la réponse à la question "Comment vérifier qu'une variable est un dictionnaire en Python?" et je n'aurais jamais pensé à chercher ma réponse dans "Différences entre isinstance () et type () en python".
lodebari
5
Pour vérifier si une variable est un dictionnaire en particulier, vous devriez probablement utiliser isinstance(v, collections.abc.Mapping). En d'autres termes, ce n'est pas un doublon exact de "Différences entre isinstance () et type ()".
Josh Kelley

Réponses:

279

Vous pourriez utiliser if type(ele) is dictou utiliser isinstance(ele, dict)ce qui fonctionnerait si vous aviez sous dict- classé :

d = {'abc':'abc','def':{'ghi':'ghi','jkl':'jkl'}}
for ele in d.values():
    if isinstance(ele,dict):
       for k, v in ele.items():
           print(k,' ',v)
Padraic Cunningham
la source
70
Je down-voté cette réponse parce que la bonne réponse à la question générale est: isinstance(ele, collections.Mapping). Il travaille pour dict(), collections.OrderedDict()et collections.UserDict(). L'exemple de la question est suffisamment précis pour que la réponse de Padriac fonctionne, mais ce n'est pas assez bon pour le cas général.
Alexander Ryzhov
4
J'ai rétrogradé cette réponse parce que si vous allez invoquer ele.items()pourquoi vérifiez-vous le type? EAFP / canard frappe fonctionne ici, juste envelopper for k,v in ele.items()dans try...except (AttributeError, TypeError). Si une exception est levée, vous savez qu'il elen'y en a pas itemsqui donne un résultat itérable ...
cowbert
@Padraic Cunningham Votre cas de bord hypothétique nécessiterait que votre classe personnalisée "100% pas un dict" 1. ait une items()méthode 2. qui se trouve justement produire un itérable et 3. où chaque élément de cet itérable peut être représenté comme un 2 -tuple. À ce stade, votre objet personnalisé a déjà implémenté la moitié d'un dict. Aux fins du code répertorié, nous ne nous sommes souciés que de l'expansion conditionnelle de l'élément imbriqué, et votre objet personnalisé l'implémente déjà. (C'est un peu ironique que vous critiquiez le commentaire de @Alexander Ryzhov pour être trop général mais que vous soulevez maintenant un cas général)
cowbert
1
@PadraicCunningham Je préfère ne pas enseigner aux gens qui ne sont pas trop familiers avec les anti-patterns Python pour commencer. Il y a très peu de cas d'utilisation en Python qui nécessitent un contrôle de typage explicite - la plupart proviennent de l'héritage d'une mauvaise implémentation pour commencer ('objets divins, surcharge des constructions standard de bibliothèque / langage, etc.) La question d'origine est elle-même un problème XY. Pourquoi l'OP doit-il vérifier le type? Parce que selon leur code, ce qu'ils veulent vraiment faire, c'est vérifier si un élément de leur collection se comporte comme une collection (des implémentations items()qui produisent un itérable imbriqué).
cowbert
4
@AlexanderRyzhov Pourquoi ne pas publier cette approche comme réponse? BTW comme Josh Kelley a commenté ci - dessus suggère collections.abc.Mapping, une note pourrait être approprié que ce sont les mêmes, mais collections.abcne sont pas disponibles avant Python 3.3. collections.MappingL'alias est toujours disponible dans Python 3.6, mais n'est pas documenté, donc probablement on devrait préférer collections.abc.Maping.
Yushin Washio
5

Comment vérifieriez-vous si une variable est un dictionnaire en Python?

Ceci est une excellente question, mais il est regrettable que la plupart des pistes de réponse upvoted avec une mauvaise recommandation, type(obj) is dict.

(Notez que vous ne devez pas non plus l'utiliser dictcomme nom de variable - c'est le nom de l'objet intégré.)

Si vous écrivez du code qui sera importé et utilisé par d'autres, ne présumez pas qu'ils utiliseront directement le dict intégré - rendant cette présomption rend votre code plus rigide et dans ce cas, créez des bogues facilement cachés qui n'erreront pas le programme en erreur .

Je suggère fortement, à des fins d'exactitude, de maintenabilité et de flexibilité pour les futurs utilisateurs, de ne jamais avoir d'expressions unidiomatiques moins flexibles dans votre code lorsqu'il existe des expressions idiomatiques plus flexibles.

isest un test d' identité d'objet . Il ne prend pas en charge l'héritage, il ne prend en charge aucune abstraction et il ne prend pas en charge l'interface.

Je vais donc fournir plusieurs options qui le font.

Soutenir l'héritage:

C'est la première recommandation que je voudrais faire, car elle permet aux utilisateurs de fournir leur propre sous - classe de dict, ou un OrderedDict, defaultdictou Counterdes collections module:

if isinstance(any_object, dict):

Mais il existe des options encore plus flexibles.

Soutenir les abstractions:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Cela permet à l'utilisateur de votre code d'utiliser sa propre implémentation personnalisée d'un mappage abstrait, qui comprend également n'importe quelle sous-classe de dict, tout en obtenant le comportement correct.

Utilisez l'interface

Vous entendez généralement les conseils de POO, "programme vers une interface".

Cette stratégie tire parti du polymorphisme ou du typage de canard de Python.

Il suffit donc d'essayer d'accéder à l'interface, d'attraper les erreurs spécifiques attendues ( AttributeErrorau cas où il n'y en aurait pas .itemset TypeErrorau cas où il ne itemsserait pas possible de les appeler) avec un repli raisonnable - et maintenant, toute classe qui implémente cette interface vous donnera ses éléments (la note a .iteritems()disparu en Python 3):

try:
    items = any_object.items()
except (AttributeError, TypeError):
    non_items_behavior(any_object)
else: # no exception raised
    for item in items: ...

Peut-être pourriez-vous penser que l'utilisation de la saisie de canard comme celle-ci va trop loin en permettant trop de faux positifs, et cela peut l'être, selon vos objectifs pour ce code.

Conclusion

Ne pas utiliser ispour vérifier les types de flux de contrôle standard. Utilisez isinstance, envisagez des abstractions comme Mappingou MutableMapping, et évitez complètement la vérification de type, en utilisant directement l'interface.

Aaron Hall
la source
3

L'OP n'a pas exclu la variable de départ, donc pour être complet, voici comment gérer le cas générique de traitement d'un dictionnaire supposé qui peut inclure des éléments comme dictionnaires.

En suivant également la méthode Python (3.8) recommandée pour tester le dictionnaire dans les commentaires ci-dessus.

from collections.abc import Mapping

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

def parse_dict(in_dict): 
    if isinstance(in_dict, Mapping):
        for k, v in in_dict.items():
            if isinstance(v, Mapping):
                for k, v in v.items():
                    print(k, v)
            else:
                print(k, v)

parse_dict(dict)
CodeMantle
la source
dict.iteritems()n'existe pas dans Python 3, vous devez utiliser à la dict.items()place.
Arkelis
@Arkelis Oups, vient de copier / coller cette partie, merci de l'avoir signalée - corrigée maintenant.
CodeMantle