Filtern Sie Elemente in einem Python-Wörterbuch, in denen Schlüssel eine bestimmte Zeichenfolge enthalten

93

Ich bin ein C-Codierer, der etwas in Python entwickelt. Ich weiß, wie man das Folgende in C macht (und daher in der C-ähnlichen Logik, die auf Python angewendet wird), aber ich frage mich, wie Python das macht.

Ich habe ein Wörterbuch d und möchte eine Teilmenge der Elemente bearbeiten. Nur diejenigen, deren Schlüssel (Zeichenfolge) eine bestimmte Teilzeichenfolge enthält.

dh die C-Logik wäre:

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

Ich stelle mir vor, die Python-Version wäre so etwas wie

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

Ich habe hier viele Beiträge zum Filtern von Wörterbüchern gefunden, konnte aber keinen finden, der genau dies beinhaltet.

Mein Wörterbuch ist nicht verschachtelt und ich verwende Python 2.7

Memo
quelle

Antworten:

180

Wie wäre es mit einem Diktatverständnis :

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

Wenn Sie es sehen, sollte es selbsterklärend sein, da es sich ziemlich gut wie Englisch liest.

Diese Syntax erfordert Python 2.7 oder höher.

In Python 3 gibt es nur dict.items(), nicht iteritems()so , dass Sie verwenden würden:

filtered_dict = {k:v for (k,v) in d.items() if filter_string in k}
Jonathon Reinhart
quelle
1
Warum nicht filtered_dict = {k:d[k] for k in d if filter_string in k}?
thefourtheye
5
@thefourtheye Ich werde vermuten, dass meine schneller ist, da die d[k]Suche nicht erfolgt.
Jonathon Reinhart
Auch, sagt er # do somethingin den Kommentaren, aber wir lassen hier ein paar Schlüssel fallen.
thefourtheye
Haben wir iteritemsin Python 3? Das glaube ich nicht. Also wäre meine Version kompatibel, nein?
thefourtheye
1
In Python 3 würden Sie ersetzen iteritemsmit items, die die gleiche wie Python 2.7 ist ist iteritems.
Jonathon Reinhart
17

Entscheiden Sie sich für das, was am besten lesbar und leicht zu warten ist. Nur weil Sie es in einer einzigen Zeile schreiben können, heißt das nicht, dass Sie es sollten. Ihre vorhandene Lösung entspricht in etwa dem, was ich verwenden würde, außer dass ich Benutzeriteritems zum Überspringen der Wertsuche verwenden würde, und ich hasse verschachtelte Wenns, wenn ich sie vermeiden kann:

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

Wenn Sie jedoch wirklich möchten, dass etwas Sie durch ein gefiltertes Diktat iterieren lässt, würde ich nicht den zweistufigen Prozess ausführen, bei dem das gefilterte Diktat erstellt und dann durchlaufen wird, sondern stattdessen einen Generator verwenden, denn was ist pythonischer (und fantastischer) als ein Generator?

Zuerst erstellen wir unseren Generator, und gutes Design schreibt vor, dass wir ihn abstrakt genug machen, um wiederverwendbar zu sein:

# 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

Und dann können wir den Generator verwenden, um Ihr Problem mit einfachem, verständlichem Code schön und sauber zu lösen:

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

Kurzum: Generatoren sind fantastisch.

Brendan F.
quelle
10

Sie können die eingebaute in verwenden Filterfunktion zum Filter Wörterbücher, Listen, usw. basierend auf bestimmten Bedingungen.

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

Der Vorteil ist, dass Sie es für verschiedene Datenstrukturen verwenden können.

Pulkit
quelle
Beachten Sie, dass items:dies item:in der Lambda-Definition enthalten sein sollte.
Bkribbs
Vielen Dank an @bkribbs für den Hinweis auf den Fehler. Ich habe es jetzt korrigiert.
Pulkit
8
input = {"A":"a", "B":"b", "C":"c"}
output = {k:v for (k,v) in input.items() if key_satifies_condition(k)}
jspurim
quelle
3
Meine Methode iteritems()wird effizienter sein als items().
Jonathon Reinhart
@ Jonathin Reinhart Ich wusste nichts davon. Vielen Dank.
Jspurim
2
Nur unter Python 2.7. In Python 3 gibt es nur items() solche, die sich wie Python 2.7 verhalten iteritems.
Jonathon Reinhart
1
Die Frage ist explizit für Python 2.7
Brendan F
7

Jonathon gab Ihnen einen Ansatz mit Diktatverständnis in seiner Antwort . Hier ist ein Ansatz, der sich mit Ihrer Aufgabe befasst .

Wenn Sie etwas mit den Werten des Wörterbuchs tun möchten, benötigen Sie überhaupt kein Wörterbuchverständnis:

Ich benutze iteritems(), seit du deine Frage mit getaggt hast

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

Das Ergebnis befindet sich nun in einer Liste, some_functiondie auf jedes Schlüssel / Wert-Paar des Wörterbuchs angewendet wird, das fooseinen Schlüssel enthält.

Wenn Sie nur mit den Werten umgehen und die Schlüssel ignorieren möchten, ändern Sie einfach das Listenverständnis:

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

some_function kann beliebig aufrufbar sein, also würde auch ein Lambda funktionieren:

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

Die innere Liste ist eigentlich nicht erforderlich, da Sie einen Generatorausdruck auch an map übergeben können:

>>> map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))
[4]
Burhan Khalid
quelle
interessant. Wie würde die some_function definiert werden? Nimmt es im ersten Fall (k, v) nur zwei Parameter an? erster Schlüssel dann Wert?
Memo
Ja, nur ein Anrufer. Also map(lambda a: a[0]*a[1], ((k,v) for k,v in {2:2, 3:2}.iteritems() if k == 2))- das wird dir geben [4].
Burhan Khalid
Dies ist richtig, aber pythonischer als die Verwendung mapist ein Listenverständnis. [f(v) for k, v in d.iteritems() if substring in k]Ich denke, es ist viel lesbarer und effizienter.
Davidmh
@memo Es würden nicht zwei Parameter benötigt, sondern ein einzelner Parameter mit zwei Elementen. Es gibt auch eine Sternenkarte, die in zwei Argumente entpackt wird, jedoch ein fauler Iterator ist (muss iteriert werden, bevor er ausgeführt wird, dh results = list(starmap(...))oder for result in starmap(...): ...).
nmclean