2011-01-15 12 views
6

mam słownika jakJedna linia wyrażenie map słownika do innego

d = {'user_id':1, 'user':'user1', 'group_id':3, 'group_name':'ordinary users'} 

i „mapowanie” słownika jak:

m = {'user_id':'uid', 'group_id':'gid', 'group_name':'group'} 

wszystkim chcę „zastąpić” klucze w pierwszym słownik z kluczami od drugiego (np. zamień "user_id" na "uid", itd.) Wiem, że klucze są niezmienne i wiem, jak to zrobić za pomocą instrukcji "if/else".

Ale może jest sposób zrobić to w jednym wyrażeniu liniowym?

+0

Jaką wersję python? – orlp

+0

uid jest wartością w drugim, a nie kluczem. Jaki jest wynik, który chcesz zobaczyć? –

Odpowiedz

15

Sure:

d = dict((m.get(k, k), v) for (k, v) in d.items()) 
+2

powinno być 'for (k, v) in d.items' not' for (k, v) in d' – mouad

+0

Naprawiono. (I ponownie naprawione; 'items' musi być wywołane jako metoda.) –

+1

Dla Pythona 2.x bardziej odpowiednie byłoby użycie' d.iteritems() 'zamiast' d.items() '(ale ma to znaczenie tylko dla naprawdę dużych słowników). –

5

W 3.x:

d = {m.get(key, key):value for key, value in d.items()} 

Działa poprzez tworzenie nowego słownika, który zawiera każdą wartość z d i mapowane do nowego klucza. Klucz jest pobierany w następujący sposób: m[key] if m in key else key, ale z domyślną funkcją .get (która obsługuje wartości domyślne, jeśli klucz nie istnieje).

+0

+1 za dodanie wersji idiomatic v3. –

0

Dlaczego chcesz zrobić to w jednym wierszu?

result = {} 
for k, v in d.iteritems(): 
    result[m.get(k, k)] = v 
+2

Ponieważ lista/dict/jakiekolwiek interpretacje są bardzo pythonesque i płonące szybko? – orlp

+0

"Pythonic", ale tak, prawie tyle. –

13

Weźmy doskonałą kod z @karlknechtel i zobaczyć, co robi:

>>> d = dict((m.get(k, k), v) for (k, v) in d.items()) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

Ale jak to działa?

Aby zbudować słownik, można użyć funkcji dict(). Oczekuje listy krotek. W wersjach 3.x i> 2.7 można również użyć słownika (patrz odpowiedź @noccracker).

Przeanalizujmy argument dyktowania. Na początku potrzebujemy listy wszystkich przedmiotów wm. Każdy element jest krotką w formacie (klucz, wartość).

>>> d.items() 
[('group_id', 3), ('user_id', 1), ('user', 'user1'), ('group_name', 'ordinary users')] 

Biorąc pod uwagę wartość klucza k, możemy uzyskać wartość klucza w prawo od m wykonując m[k].

>>> k = 'user_id' 
>>> m[k] 
'uid' 

Niestety, nie wszystkie klawisze d również występować w m.

>>> k = 'user' 
>>> m[k] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
KeyError: 'user' 

Aby obejść ten problem, można użyć d.get(x, y), która zwraca d[x] jeśli klucz x istnieje, lub wartość domyślną y czy nie. Teraz, jeśli klucz k z d nie istnieje w m, po prostu go przechowujemy, więc domyślnie jest to k.

>>> m.get(k, k). 
'user' 

Teraz jesteśmy gotowi, by zbudować listę krotek w celu dostarczenia do dict(). Aby zbudować listę w jednym wierszu, możemy użyć list comprehension.

Aby zbudować listę kwadratów, należy napisać to:

>>> [x**2 for x in range(5)] 
[0, 1, 4, 9, 16] 

w naszym przypadku wygląda to tak:

>>> [(m.get(k, k), v) for (k, v) in d.items()] 
[('gid', 3), ('uid', 1), ('user', 'user1'), ('group', 'ordinary users')] 

To łyk, przyjrzyjmy się jeszcze raz.

Daj mi listę [...], który składa się z krotek:

[(.., ..) ...] 

chcę jeden krotki dla każdego elementu x w d:

[(.., ..) for x in d.items()] 

Wiemy, że każdy element jest krotką z dwoma komponenty, dzięki czemu możemy rozszerzyć je do dwóch zmiennych: k i v.

[(.., ..) for (k, v) in d.items()] 

Każda krotka powinien mieć prawo klucz z m jako pierwszy składnik, lub k jeśli k nie istnieje w m, a wartość z d.

[(m.get(k, k), v) for (k, v) in d.items()] 

Możemy przekazać to jako argument do dict().

>>> dict([(m.get(k, k), v) for (k, v) in d.items()]) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

Wygląda dobrze! Ale czekaj, możesz powiedzieć, @karlknechtel nie używał nawiasów kwadratowych.

W porządku, nie użył listy ze zrozumieniem, ale jako generator expression. Mówiąc po prostu, różnica polega na tym, że zrozumienie listy buduje całą listę w pamięci, podczas gdy wyrażenie generatora oblicza element na raz. Jeśli lista na stronach służy jako wynik pośredni, zazwyczaj dobrym pomysłem jest użycie wyrażenia generatora. W tym przykładzie to naprawdę nie robi różnicy, ale przyzwyczajenie się do tego jest dobrym nawykiem.

Odpowiedniki wyrażeń generator wygląda następująco:

>>> ((m.get(k, k), v) for (k, v) in d.items()) 
<generator object <genexpr> at 0x1004b61e0> 

Jeśli zdasz wyrażenie generatora jako argument do funkcji, zazwyczaj można pominąć zewnętrznych nawiasów. Na koniec otrzymujemy:

>>> dict((m.get(k, k), v) for (k, v) in d.items()) 
{'gid': 3, 'group': 'ordinary users', 'uid': 1, 'user': 'user1'} 

W jednej linii kodu dzieje się całkiem sporo. Niektórzy twierdzą, że jest to nieczytelne, ale gdy już się do tego przyzwyczaicie, rozciągnięcie tego kodu na kilka linii wydaje się nieczytelne. Tylko nie przesadzaj. Zrozumienie listy i wyrażeń generatora jest bardzo potężne, ale z wielką siłą wiąże się wielka odpowiedzialność. +1 na dobre pytanie!