2010-07-12 21 views
20

Ho appena appreso la comprensione delle liste, che è un ottimo modo veloce per ottenere dati in una singola riga di codice. Ma qualcosa mi sta infastidendo.Python list comprehension for dictionaries nei dizionari?

Nella mia prova ho questo tipo di dizionari all'interno della lista:

[{'y': 72, 'x': 94, 'fname': 'test1420'}, {'y': 72, 'x': 94, 'fname': 'test277'}] 

La lista di comprensione s = [ r for r in list if r['x'] > 92 and r['x'] < 95 and r['y'] > 70 and r['y'] < 75 ] funziona perfettamente su quella (è, infatti, il risultato di questa linea)

Comunque , Ho poi capito che non sto davvero usando una lista nel mio altro progetto, sto usando un dizionario. In questo modo:

{'test1420': {'y': '060', 'x': '070', 'fname': 'test1420'}} 

In questo modo posso semplicemente modificare il mio dizionario con var['test1420'] = ...

Ma list comprehension non lavorare su questo! E non posso modificare gli elenchi in questo modo perché non è possibile assegnare un indice del genere.

C'è un altro modo?

+1

La comprensione delle liste può essere interessante, ma non esagerare. Non esiste una regola costante contro le comprensioni giganti, ma per quello che vale, la Guida di stile Python di Google consiglia contro qualsiasi cosa eccessivamente complessa: http://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=List_Comprehensions#List_Comprehensions –

risposta

36

Si può fare questo:

s = dict([ (k,r) for k,r in mydict.iteritems() if r['x'] > 92 and r['x'] < 95 and r['y'] > 70 and r['y'] < 75 ]) 

Questo richiede un dict come specificato e restituisce una dict 'filtrato'.

+0

Funziona allo stesso modo della soluzione di Tryptich, ma l'utilizzo di iteritems è davvero più veloce. Grazie! – skerit

+0

è possibile renderlo ancora più veloce se si gira '[]' in a '()' come dict può prendere un generatore come argomento (se l'espressione generatore è l'unico argomento come in questo caso anche il '()' può essere escluso) –

8

Se dct è

{'test1420': {'y': '060', 'x': '070', 'fname': 'test1420'}, 
'test277': {'y': 72, 'x': 94, 'fname': 'test277'},} 

Forse siete alla ricerca di qualcosa di simile a:

[ subdct for key,subdct in dct.iteritems() 
    if 92<subdct['x']<95 and 70<subdct['y']<75 ] 

Un po 'di delicatezza è che Python permette di disuguaglianze a catena:

92<dct[key]['x']<95 

invece di

if r['x'] > 92 and r['x'] < 95 

Si noti inoltre che sopra ho scritto una lista di comprensione, quindi si ottiene una lista (in questo caso, di dicts).

In python3 ci sono cose come dict comprehensions così:

{ n: n*n for n in range(5) } # dict comprehension 
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16} 

Nel python2 l'equivalente sarebbe

dict((n,n*n) for n in range(5)) 

Non sono sicuro se siete alla ricerca di un elenco di dicts o una dict of dicts, ma se capisci gli esempi sopra, è facile modificare la mia risposta per ottenere quello che vuoi.

0

È possibile ottenere un elenco dei valori di un dizionario d con d.values(). La tua comprensione delle liste dovrebbe funzionare usando questo, anche se sono un po 'poco chiaro che cosa esattamente vuoi che sia l'output.

2

Sembra che si desidera qualcosa di simile:

my_dict = {'test1420': {'y': '060', 'x': '070', 'fname': 'test1420'}, 
      'test277' : {'y': '072', 'x': '094', 'fname': 'test277'}} 


new_dict = dict((k,v) for k,v in my_dict.items() 
        if 92 < int(v['x']) < 95 and 70 < int(v['y']) < 75) 

Alcune note su questo codice:

  1. sto usando un generatore di espressione invece di una comprensione lista
  2. Python consente di combinare disuguaglianza test come low < value < high
  3. Il costruttore dict() prende un iterable di chiave tuple/valore per creare una dizionario
+0

Funziona e ha lo stesso aspetto della soluzione di adamk. Eppure usare iteritems è un po 'più veloce di questo. Entrambi sono più lenti della normale comprensione di lista, però: P Grazie per il suggerimento di combinazione! – skerit

0

C'è un altro modo?

Perché non considerare l'uso di alcuni oggetti leggeri?

È possibile ancora utilizzare la comprensione di elenchi per raccogliere o filtrare gli oggetti e ottenere molto in termini di chiarezza/estensibilità.

>>> class Item(object): 
...  def __init__(self, x, y, name): 
...   self.x = x 
...   self.y = y 
...   self.name = name 
... 
>>> list_items = [] 
>>> list_items.append(Item(x=70, y=60, name='test1420'))       
>>> list_items.append(Item(x=94, y=72, name='test277'))       
>>> items_matching = [item for item in list_items 
         if 92 < item.x < 95 and 70 < item.y < 75] 
>>> for item in items_matching: 
...  print item.name 
... 
test277 
>>> first_item = items_matching[0] 
>>> first_item.x += 50 
>>> first_item.x 
144 
Problemi correlati