2016-04-26 3 views
4

Nehmen Sie ein Wörterbuchein Wörterbuch Wert bekommen, wo ein Teil des Schlüssels in einem String ist

dict = {"word":{"a":{"b":2}}} 

ich den Wert 2. Um darauf zuzugreifen bekommen möchte ich diese dict["word"]["a"]["b"] tun können

Allerdings möchte ich zu wissen, ich kann einen Teil des Schlüssels (der ["a"]["b] Abschnitt) in einem String

Und siehe es so etwas wie diese

habe 0

Oder so etwas

Ich weiß, dass diese Syntax falsch ist, aber ist es möglich, etwas ähnliches zu tun?

in Python 3.x

EDIT: Ein besseres dict Beispiel kann dieser

dict = { 
"word":{"a":{"b":2}} 
"hello":{"a":{"b":1}} 
"mouse":{"a":{"b":5}} 
} 

Wie jeweils den Wert von b bekommen? wo Sie hartes Kodieren im ["a"]["b"] Bit des Schlüssels vermeiden möchten, falls es sich ändern könnte.

+2

In welchem ​​Kontext diese verwenden ist? – mattsap

+1

Dies könnte ein Fall sein "xy-Problem" - auf verschachtelte Wörterbuchelemente über eine Zeichenkette auf diese Weise zu verweisen ist nicht möglich, aber was Sie damit tun wollen, ist wahrscheinlich möglich ... auf andere Weise. Also, was willst du damit machen? – Roberto

+0

Python hat eine 'eval'-Funktion, die wahrscheinlich funktionieren könnte - aber ich stimme den vorherigen Kommentaren zu. – usr2564301

Antwort

3

Verwenden Sie nicht dict als Variablenname, da diese die integrierte Funktion überschattet.


Eine der Möglichkeiten, es zu tun, um die re module verwendet. Die Hauptidee hier ist, dass Sie einen regulären Ausdruck verwenden, um die Schlüssel herauszufinden. Sobald Sie die Liste der Schlüssel haben, ist das Extrahieren von Daten eine einfache Aufgabe.

>>> d = {"word":{"a":{"b":2}}} 
>>> s = '["a"]["b"]' 
>>> import re 
>>> keys = re.findall(r'\["(.+?)"\]',s) 
>>> d["word"][keys[0]][keys[1]] 
2 

Wenn es viele Schlüssel sind, dann können Sie reduce aus functools Paket verwenden.

>>> import functools 
>>> functools.reduce(dict.__getitem__, keys, d['word']) 
2 

Für längere dicts (als JonClements in einem comment erwähnt hat), können Sie ein list comprehension

>>> d = { 
... "word":{"a":{"b":2}}, 
... "hello":{"a":{"b":1}}, 
... "mouse":{"a":{"b":5}} 
... } 
>>> [functools.reduce(dict.__getitem__, keys, d[i]) for i in d] 
[2, 5, 1] 

jedoch beachten, dass verwenden aufgrund der ungeordneten Natur des Dictionary-Schlüssels, die Ausgabe der Liste möglicherweise nicht wie erwartet. Ein besserer Weg ist es, das komplette Wörterbuch auf einen neuen (unter Verwendung eines dict Verständnis), wie in

>>> {i:functools.reduce(dict.__getitem__, keys, d[i]) for i in d} 
{'word': 2, 'mouse': 5, 'hello': 1} 
+0

Natürlich - sobald Sie die Schlüssel (in welcher Syntax) extrahiert haben, können Sie anpassen: 'functools.reduce (dict .__ getitem__, 'ab', d ['Wort']) 'um das flexibler zu machen ... muss man einfach das '' ab'' in einer netten Syntax bekommen (vielleicht sogar etwas von' input' nehmen und 'ast.literal_eval' darauf laufen lassen ...) –

+0

Wahrscheinlich machen wollen die Tasten eine Liste/Tupel stattdessen (dh: behalten Sie Ihre ursprünglichen 're.findall'' Schlüssel') eines Strings für den Fall, dass es Multi-Char/Non-String-Schlüssel :) –

+0

Und natürlich - wenn Sie die Liste-Comp verwenden Reihenfolge der Werte (vorausgesetzt, sie sind wichtig) kann überraschend sein - es sei denn, die Quelle ist ein 'OrderedDict' :) –

2

Der beste Weg, zu reduzieren, ist Ihre eigene Syntax zu schreiben, die Sie analysieren würde. Sie müssen einen Zeichen verwenden, der nie in der Taste erscheint, ein beliebtes Beispiel ist ., aber ähnlich, $, | funktionieren auch.

def get(d, key): 
    kp = key.split('.') 
    for k in kp: 
     d = d[k] 
    return d 

d = {'a': {'b': 2}} 
get(d, 'a.b') 
>>> 2 

EDIT für das Update auf die Frage:

So haben Sie eine Liste von Werten von a.b in der Lage sein wollen, in jedem dieser Fälle speziell zu bekommen?wenn so können Sie immer noch die oben beschriebene Methode verwenden, wie so

d = { 
"word":{"a":{"b":2}}, 
"hello":{"a":{"b":1}}, 
"mouse":{"a":{"b":5}}, 
} 

# nesting calls to get just to show it is flexible like that 
[get(get(d, k), 'a.b') for k in d.keys()] 
# if you wanted to generate a dict of {"word": val} it would look like this 
{k:get(get(d, k), 'a.b') for k in d.keys()} 

es funktioniert auch, wenn Ihr variable Teil in der Mitte ist

d = { 
"x": {"word":{"a":{"b":2}}}, 
"x": {"hello":{"a":{"b":1}}}, 
"x": {"mouse":{"a":{"b":5}}}, 
} 

# using more calls to get just to show it is flexible like that 
[get(get(d, 'x.'+k), 'a.b') for k in get(d, 'x').keys()] 
Verwandte Themen