Come verificare se una variabile è un dizionario in Python?


214

Come controlleresti se una variabile è un dizionario in Python?

Ad esempio, vorrei che eseguisse il ciclo attraverso i valori nel dizionario fino a quando non trova un dizionario. Quindi, scorrere attraverso quello che trova:

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)

Anche stackoverflow.com/questions/378927/... (che è contrassegnato come duplicato di quello sopra).
NPE,


40
No, non è la stessa domanda. La risposta a questa domanda e alle altre domande qui elencate contengono sostanzialmente le stesse informazioni. Ma la risposta a "Come verificare se una variabile è un dizionario in Python" è "Usa type () o isinstance ()" che porta quindi a una nuova domanda, qual è la differenza tra type () e isinstance () . Ma la persona che pone la prima domanda non può sapere che fino alla prima risposta. Ergo, domande diverse, che contano quando cerchi la tua domanda sul sito.

13
Sono d'accordo con @mbakeranalecta Sono venuto qui cercando la risposta alla domanda "Come verificare se una variabile è un dizionario in Python?" e non avrei mai pensato di guardare la mia risposta in "Differenze tra isinstance () e type () in Python".
lodebari,

5
Per verificare se una variabile è un dizionario in particolare, probabilmente dovresti usare isinstance(v, collections.abc.Mapping). In altre parole, questo non è un duplicato esatto di "Differenze tra isinstance () e type ()."
Josh Kelley,

Risposte:


279

È possibile utilizzare if type(ele) is dicto utilizzareisinstance(ele, dict) che funzionerebbe se si fosse eseguito la sottoclasse dict:

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)

70
I down-votato questa risposta perché la risposta giusta alla domanda generale è: isinstance(ele, collections.Mapping). Si lavora per dict(), collections.OrderedDict()e collections.UserDict(). L'esempio nella domanda è abbastanza specifico per la risposta di Padriac al lavoro, ma non è abbastanza buono per il caso generale.
Alexander Ryzhov,

4
Ho votato in negativo questa risposta perché se hai intenzione di invocare ele.items()perché stai controllando il tipo? EAFP / anatra tipizzazione lavora qui, basta avvolgere for k,v in ele.items()in try...except (AttributeError, TypeError). Se viene sollevata un'eccezione, sai che elenon ha un itemsrisultato iterabile ...
Cowbert,

@Padraic Cunningham Il tuo ipotetico caso limite richiederebbe che la tua classe personalizzata "100% not a dict" 1. avesse un items()metodo 2. che in tal caso produceva un iterabile e 3. dove ogni elemento di quel iterabile può essere rappresentato come un 2 -tuple. A questo punto il tuo oggetto personalizzato ha già implementato già metà di un dict. Ai fini del codice elencato ci siamo preoccupati solo dell'espansione condizionale dell'elemento nidificato e l'oggetto personalizzato lo implementa già. (È un po 'ironico criticare il commento di @Alexander Ryzhov per essere troppo generale ma ora sollevare un caso generale)
cowbert,

1
@PadraicCunningham Preferirei non insegnare alle persone che non sono eccessivamente familiari con gli anti-schemi Python per cominciare. Ci sono pochissimi casi d'uso in Python che richiedono un controllo esplicito dei caratteri - la maggior parte deriva dall'ereditare un'implementazione errata per cominciare ('oggetto di dio, costrutti di libreria / linguaggio standard prevalenti, ecc.) La domanda originale è di per sé un problema XY. Perché l'OP deve controllare il tipo? Perché secondo il loro codice ciò che vogliono veramente fare è controllare se un elemento nella loro collezione si comporta come una raccolta (strumenti items()che producono un iterabile annidato).
cowbert,

4
@AlexanderRyzhov Perché non pubblicare questo approccio come risposta? A proposito come Josh Kelley ha commentato sopra suggerendo collections.abc.Mapping, una nota potrebbe essere appropriata che siano uguali, ma collections.abcnon è disponibile prima di Python 3.3. collections.Mappingl'alias è ancora disponibile in Python 3.6, ma non è documentato, quindi probabilmente si dovrebbe preferire collections.abc.Maping.
Yushin Washio,

5

Come verifichi se una variabile è un dizionario in Python?

Questa è un'ottima domanda, ma è un peccato che la risposta più votata porti a una cattiva raccomandazione type(obj) is dict.

(Nota che non dovresti usare anche dictcome nome di variabile - è il nome dell'oggetto incorporato.)

Se stai scrivendo codice che verrà importato e utilizzato da altri, non presumere che utilizzeranno direttamente il comando incorporato dict - rendendo tale presunzione rende il tuo codice più inflessibile e, in questo caso, crea bug facilmente nascosti che non rovinerebbero il programma .

Suggerisco vivamente, ai fini della correttezza, della manutenibilità e della flessibilità per i futuri utenti, di non avere mai espressioni meno flessibili e unidiomatiche nel codice quando ci sono espressioni più flessibili e idiomatiche.

isè un test per l' identità dell'oggetto . Non supporta l'ereditarietà, non supporta alcuna astrazione e non supporta l'interfaccia.

Quindi fornirò diverse opzioni che lo fanno.

Eredità di supporto:

Questa è la prima raccomandazione che vorrei fare, perché permette agli utenti di fornire le proprie sottoclasse di dict, o una OrderedDict, defaultdicto Counterdal modulo collezioni:

if isinstance(any_object, dict):

Ma ci sono opzioni ancora più flessibili.

Astrazioni di supporto:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Ciò consente all'utente del codice di utilizzare la propria implementazione personalizzata di una mappatura astratta, che include anche qualsiasi sottoclasse di dict, e ottenere comunque il comportamento corretto.

Usa l'interfaccia

Di solito ascolti il ​​consiglio OOP, "programma su un'interfaccia".

Questa strategia sfrutta il polimorfismo o la tipizzazione delle anatre di Python.

Quindi prova ad accedere all'interfaccia, rilevando gli errori previsti specifici ( AttributeErrornel caso non ce ne sia .itemse TypeErrornel caso itemsnon sia richiamabile) con un ragionevole fallback - e ora qualsiasi classe che implementa quell'interfaccia ti darà i suoi elementi (la nota .iteritems()è andata in Python 3):

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

Forse potresti pensare che usare la tipizzazione anatra in questo modo esca troppo nel consentire troppi falsi positivi, e potrebbe essere, a seconda dei tuoi obiettivi per questo codice.

Conclusione

Non utilizzare isper controllare i tipi per il flusso di controllo standard. Usa isinstance, considera le astrazioni come Mappingo MutableMappinge considera di evitare del tutto il controllo del tipo, usando direttamente l'interfaccia.


3

L'OP non ha escluso la variabile iniziale, quindi per completezza ecco come gestire il caso generico di elaborazione di un dizionario presunto che può includere elementi come dizionari.

Seguendo anche il puro metodo consigliato da Python (3.8) per testare il dizionario nei commenti sopra.

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)

dict.iteritems()non esiste in Python 3, dovresti dict.items()invece usarlo .
Arkelis,

@Arkelis Oops, ho appena copiato / incollato quella parte, grazie per averlo sottolineato - corretto ora.
CodeMantle
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.