filtra gli elementi in un dizionario Python dove le chiavi contengono una stringa specifica


95

Sono un programmatore C che sviluppa qualcosa in Python. So come fare quanto segue in C (e quindi nella logica C-like applicata a Python), ma mi chiedo quale sia il modo "Python" di farlo.

Ho un dizionario d, e vorrei operare su un sottoinsieme degli elementi, solo quelli la cui chiave (stringa) contiene una sottostringa specifica.

cioè la logica C sarebbe:

for key in d:
    if filter_string in key:
        # do something
    else
        # do nothing, continue

Immagino che la versione in Python sarebbe qualcosa di simile

filtered_dict = crazy_python_syntax(d, substring)
for key,value in filtered_dict.iteritems():
    # do something

Ho trovato molti post qui riguardo al filtraggio dei dizionari, ma non sono riuscito a trovarne uno che riguardasse esattamente questo.

Il mio dizionario non è annidato e sto usando Python 2.7



Risposte:


182

Che ne dici di una comprensione dei dettami :

filtered_dict = {k:v for k,v in d.iteritems() if filter_string in k}

Uno lo vedi, dovrebbe essere autoesplicativo, poiché si legge abbastanza bene come l'inglese.

Questa sintassi richiede Python 2.7 o versione successiva.

In Python 3, c'è solo dict.items(), non iteritems()così useresti:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}

1
Perché no filtered_dict = {k:d[k] for k in d if filter_string in k}?
quarto

5
@thefourtheye Immagino che il mio sia più veloce, poiché non incorre nella d[k]ricerca.
Jonathon Reinhart

Inoltre, dice # do somethingnei commenti, ma qui rilasciamo alcune chiavi.
quattro

Abbiamo iteritemsin Python 3? Non credo proprio. Quindi, la mia versione sarebbe compatibile, no?
quarto

1
In Python 3 dovresti sostituire iteritemscon items, che è lo stesso di Python 2.7 iteritems.
Jonathon Reinhart

17

Scegli tutto ciò che è più leggibile e facilmente gestibile. Solo perché puoi scriverlo in una singola riga non significa che dovresti. La tua soluzione esistente è simile a quella che userei se non utilizzerei gli iteritem per saltare la ricerca del valore e odio i if nidificati se posso evitarli:

for key, val in d.iteritems():
    if filter_string not in key:
        continue
    # do something

Tuttavia, se vuoi davvero qualcosa che ti permetta di iterare attraverso un dict filtrato, non farei il processo in due fasi di costruire il dict filtrato e quindi iterarlo attraverso di esso, ma invece utilizzare un generatore, perché ciò che è più pitonico (e fantastico) di un generatore?

Per prima cosa creiamo il nostro generatore e un buon design impone di renderlo abbastanza astratto da essere riutilizzabile:

# The implementation of my generator may look vaguely familiar, no?
def filter_dict(d, filter_string):
    for key, val in d.iteritems():
        if filter_string not in key:
            continue
        yield key, val

E poi possiamo usare il generatore per risolvere il tuo problema in modo piacevole e pulito con un codice semplice e comprensibile:

for key, val in filter_dict(d, some_string):
    # do something

In breve: i generatori sono fantastici.


11

È possibile utilizzare la funzione di filtro incorporata per filtrare dizionari, elenchi, ecc. In base a condizioni specifiche.

filtered_dict = dict(filter(lambda item: filter_str in item[0], d.items()))

Il vantaggio è che puoi usarlo per diverse strutture di dati.


Nota che items:dovrebbe essere item:nella definizione lambda.
bkribbs

Grazie @bkribbs per aver segnalato l'errore. L'ho risolto ora.
Pulkit

8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}

3
Il mio metodo di utilizzo iteritems()sarà più efficiente di items().
Jonathon Reinhart

@Jonathin Reinhart Non lo sapevo. Grazie.
jspurim

2
Solo su Python 2.7. In Python 3 c'è solo items() , che si comporta come Python 2.7 iteritems.
Jonathon Reinhart

1
La domanda è esplicitamente per python 2.7
Brendan F

7

Jonathon ti ha fornito un approccio utilizzando la comprensione dei dettami nella sua risposta . Ecco un approccio che si occupa della tua parte di fare qualcosa .

Se vuoi fare qualcosa con i valori del dizionario, non hai affatto bisogno di una comprensione del dizionario:

Sto usando iteritems() dato che hai taggato la tua domanda con

results = map(some_function, [(k,v) for k,v in a_dict.iteritems() if 'foo' in k])

Ora il risultato sarà in un elenco con some_functionapplicato a ciascuna coppia chiave / valore del dizionario, che ha foonella sua chiave.

Se vuoi solo gestire i valori e ignorare le chiavi, cambia semplicemente la comprensione dell'elenco:

results = map(some_function, [v for k,v in a_dict.iteritems() if 'foo' in k])

some_function può essere qualsiasi chiamabile, quindi anche un lambda funzionerebbe:

results = map(lambda x: x*2, [v for k,v in a_dict.iteritems() if 'foo' in k])

L'elenco interno in realtà non è richiesto, poiché puoi anche passare un'espressione del generatore da mappare:

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]

interessante. come sarebbe definita la funzione some_? nel primo caso (k, v), ci vogliono solo due parametri? prima chiave poi valore?
nota

Sì, solo un callable. Quindi map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2)), questo ti darà [4].
Burhan Khalid

Questo è corretto, ma più pitonico che usare mapè una comprensione delle liste. [f(v) for k, v in d.iteritems() if substring in k]Penso che sia molto più leggibile e più efficiente.
Davidmh

@memo Non ci vorranno due parametri, ci vorrebbe un singolo parametro con due elementi. Esiste anche una mappa stellare che verrà decompressa in due argomenti, tuttavia è un iteratore pigro (deve essere iterato prima di essere eseguito, ovvero results = list(starmap(...))o for result in starmap(...): ...).
nmclean
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.