2011-01-15 10 views
6

ho dizionario comeespressione One-line per mappare dizionario ad un altro

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

e dizionario "mappatura", come:

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

tutto quello che voglio "sostituire" chiavi in ​​primo dizionario con chiavi dal secondo (es. sostituire 'user_id' con 'uid', ecc.) So che le chiavi sono immutabili e so come farlo con l'istruzione 'if/else'.

Ma forse c'è un modo per farlo in un'espressione di linea?

+0

Quale versione di python? – orlp

+0

uid è il valore nel secondo, non nella chiave. Qual è l'output che ti piace vedere? –

risposta

15

Certo:

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

dovrebbe essere 'for (k, v) in d.items' not' for (k, v) in d' – mouad

+0

corretto. (E ri-fissato; 'item' deve essere chiamato come metodo.) –

+1

Per Python 2.x, sarebbe più appropriato usare' d.iteritems() 'invece di' d.items() '(ma questo è importante solo per i dizionari veramente grandi). –

5

In 3.x:

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

Funziona con la creazione di un nuovo dizionario che contiene ogni valore da d e associata a una nuova chiave. La chiave viene recuperata in questo modo: m[key] if m in key else key, ma poi con la funzione .get predefinita (che supporta i valori predefiniti se la chiave non esiste).

+0

+1 per aggiungere la versione v3 idiomatica. –

0

Perché dovresti farlo in una riga?

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

Perché la lista/dict/qualsiasi comprensione è molto poderosa e veloce? – orlp

+0

"Pythonic", ma sì, più o meno quello. –

13

Prendiamo il codice eccellente da @karlknechtel e vedere che cosa fa:

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

Ma come funziona?

Per creare un dizionario, è possibile utilizzare la funzione dict(). Si aspetta una lista di tuple. In 3.xe> 2.7, è anche possibile utilizzare la comprensione del dizionario (vedere answer by @nightcracker).

Analizziamo l'argomento del ditt. All'inizio abbiamo bisogno di un elenco di tutti gli elementi in m. Ogni elemento è una tupla nel formato (chiave, valore).

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

dato un valore chiave k, siamo riusciti a ottenere il valore della chiave giusta da m facendo m[k].

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

Purtroppo, non esistono anche tutte le chiavi in ​​d in m.

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

Per aggirare questo, si può utilizzare d.get(x, y), che restituisce d[x] se la chiave x esiste, o il valore di default y se non lo fa. Ora, se non esiste una chiaveda d in m, l'impostazione predefinita è .

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

Ora siamo pronti a costruire una lista di tuple di fornire ai dict(). Per costruire una lista in una riga, possiamo usare list comprehension.

Per costruire un elenco di piazze, si potrebbe scrivere questo:

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

Nel nostro caso, sembra che questo:

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

Questo è un boccone, diamo un'occhiata a che ancora una volta.

Dammi un elenco [...], che si compone di tuple:

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

voglio una tupla per ogni elemento x in d:

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

Sappiamo che ogni elemento è una tupla con due componenti, quindi possiamo estenderlo a due variabili k e v.

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

Ogni tupla deve avere la giusta chiave da m come primo componente, oppure k se k non esiste in m, e il valore di d.

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

Possiamo passarlo come argomento a dict().

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

Sembra buono! Ma aspetta, potresti dire, @karlknechtel non ha usato parentesi quadre.

Giusto, non ha usato una comprensione di lista, ma uno generator expression. Semplicemente parlando, la differenza è che una lista di comprensione costruisce l'intera lista in memoria, mentre un'espressione di generatore calcola sull'oggetto alla volta. Se una lista su serve come risultato intermedio, di solito è una buona idea usare un'espressione di generatore. In questo esempio, non fa davvero la differenza, ma è una buona abitudine abituarsi.

Le espressioni generatore equivalenti aspetto:

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

Se si passa un generatore di espressione come argomento di una funzione, di solito si può omettere le parentesi esterne. Infine, otteniamo:

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

Accade molto in una riga di codice. Alcuni dicono che questo è illeggibile, ma una volta che vi siete abituati, allungare questo codice su più righe sembra illeggibile. Basta non esagerare. La comprensione delle liste e le espressioni generatrici sono molto potenti, ma con un grande potere derivano grandi responsabilità. +1 per una buona domanda!

Problemi correlati