Holen Sie sich eine Teilmenge eines Python-Wörterbuchs

73

Ich habe ein Wörterbuch:

{'key1':1, 'key2':2, 'key3':3}

Ich muss einen Teil dieses Wörterbuchs an Code von Drittanbietern übergeben. Es möchte nur ein Wörterbuch, das Schlüssel enthält, ['key1', 'key2', 'key99']und wenn es einen anderen Schlüssel erhält (z. B. 'key3'), explodiert es in einem bösen Durcheinander. Der betreffende Code liegt außerhalb meiner Kontrolle, sodass ich in einer Position bin, in der ich mein Wörterbuch bereinigen muss.

Was ist der beste Weg, um ein Wörterbuch auf einen Satz von Schlüsseln zu beschränken?

Angesichts des obigen Beispielwörterbuchs und der zulässigen Schlüssel möchte ich:

{'key1':1, 'key2':2}
Oli
quelle

Antworten:

76
In [38]: adict={'key1':1, 'key2':2, 'key3':3}
In [41]: dict((k,adict[k]) for k in ('key1','key2','key99') if k in adict)
Out[41]: {'key1': 1, 'key2': 2}

In Python3 (oder python2.7 oder höher) können Sie es mit einem tun dict-Verständnis zu:

>>> {k:adict[k] for k in ('key1','key2','key99') if k in adict}
{'key2': 2, 'key1': 1}
unutbu
quelle
8
Das Diktatverständnis ist auch in Python 2.7 enthalten.
Morgan Harris
Ich wünschte, es gäbe einen Betreiber dafür. Likeadict - {'key1'} == {'key2':2, 'key3':3}
wim
Es scheint keine vernünftige Umkehrung der dict.updateMethode zu geben
wim
3
{k: v für k, v in adict.items (), wenn k in ('key1', 'key2', 'key99')} auch gut funktioniert. Nur ein kleiner Unterschied.
jlimahaverford
1
Um die Dinge einfacher betrachten {k: v for k, v in adict.items() if k in new_keys}vs. {k:adict[k] for k in new_keys if k in adict}. Stehen wir in letzterem Fall nicht vor einem ähnlichen Problem, wenn new_keys groß ist?
jlimahaverford
23
dict(filter(lambda i:i[0] in validkeys, d.iteritems()))
Björn Pollex
quelle
4
Für Python 3 sollte das so sein d.items().
kirbyfan64sos
9

Verwenden Sie in modernem Python (2.7 +, 3.0 +) ein Wörterbuchverständnis:

d = {'key1':1, 'key2':2, 'key3':3}
included_keys = ['key1', 'key2', 'key99']

{k:v for k,v in d.items() if k in included_keys}
Watsonic
quelle
Wenn ich das richtig verstehe, wäre dies in diesem Fall weniger effizient len(d) >> len(included_keys)( >>bedeutet "viel größer"). Aber ich denke im allgemeinen Fall ist len ​​(d) nicht sehr groß ...
Oren Milman
4

Eine andere Lösung ohne diktiertes Verständnis.

>>> a = {'key1':1, 'key2':2, 'key3':3}
>>> b = {'key1':1, 'key2':2}
>>> { k:a[k] for k in b.keys()}
{'key2': 2, 'key1': 1}
Nazime Lakehal
quelle
3

Mein Weg dies zu tun ist.

from operator import itemgetter

def subdict(d, ks):
    return dict(zip(ks, itemgetter(*ks)(d)))

my_dict = {'key1':1, 'key2':2, 'key3':3}

subdict(my_dict, ['key1', 'key3'])

Aktualisieren

Ich muss allerdings zugeben, dass die obige Implementierung den Fall nicht behandelt, wenn die Länge ks0 oder 1 ist. Der folgende Code behandelt die Situation und ist kein Einzeiler mehr.

def subdict(d, ks):
    vals = []
    if len(ks) >= 1:
        vals = itemgetter(*ks)(d)
        if len(ks) == 1:
            vals = [vals]
    return dict(zip(ks, vals))
Lei Zhao
quelle
Ich werde oft from operator import itemgetter as igein bisschen horizontalen Zeilenraum sparen und tippen. Ich glaube nicht, dass ich mich daran erinnerte, dass itemgettermehrere Gegenstände zum Abrufen benötigt würden. Tolle Erinnerung, danke!
MrDrFenner
0

Mit einer komplexen Klasse Myclassals Unterklasse von collections.UserDict. Um eine Teilmenge davon auszuwählen, dh alle Containereigenschaften beizubehalten, ist es zweckmäßig, eine Methode zu definieren, z. B. subwie folgt benannt:

def sub(self, keys):
    subset = Myclass() # no arguments; works if defined with only keyword arguments
    for key in keys:
        subset[key] = self[key]
    return subset

Es wird dann verwendet als Myclass.sub([key1, key2 ...])

theo olsthoorn
quelle