2009-04-26 12 views
6

Sto tentando di prendere un elenco di oggetti e di trasformare quell'elenco in un dettato. I valori dict sarebbero ogni oggetto nell'elenco e le chiavi dict sarebbero un valore trovato in ogni oggetto.Il modo migliore per trasformare una lista in un dettato, in cui le chiavi sono un valore di ogni oggetto?

Ecco alcuni codice che rappresenta quello che sto facendo:

class SomeClass(object): 

    def __init__(self, name): 
     self.name = name 

object_list = [ 
    SomeClass(name='a'), 
    SomeClass(name='b'), 
    SomeClass(name='c'), 
    SomeClass(name='d'), 
    SomeClass(name='e'), 
] 

object_dict = {} 
for an_object in object_list: 
    object_dict[an_object.name] = an_object 

Ora che il codice funziona, ma è un po 'brutto, e un po' lento. Qualcuno potrebbe dare un esempio di qualcosa che è più veloce/"migliore"?

modifica: OK, grazie per le risposte. Devo dire che sono sorpreso di vedere i modi più platonici sembrano più lenti di quelli fatti a mano.

edit2: OK, ho aggiornato il codice di test per renderlo un po 'più leggibile, con così tanti test heh.

Qui è dove siamo in termini di codice, ho messo gli autori nel codice e se ne ho incasinato per favore fatemelo sapere.

from itertools import izip 
import timeit 

class SomeClass(object): 

    def __init__(self, name): 
     self.name = name 

object_list = [] 

for i in range(5): 
    object_list.append(SomeClass(name=i)) 

def example_1(): 
    'Original Code' 
    object_dict = {} 
    for an_object in object_list: 
     object_dict[an_object.name] = an_object 

def example_2(): 
    'Provided by hyperboreean' 
    d = dict(zip([o.name for o in object_list], object_list)) 

def example_3(): 
    'Provided by Jason Baker' 
    d = dict([(an_object.name, an_object) for an_object in object_list]) 

def example_4(): 
    "Added izip to hyperboreean's code, suggested by Chris Cameron" 
    d = dict(izip([o.name for o in object_list], object_list)) 

def example_5(): 
    'zip, improved by John Fouhy' 
    d = dict(zip((o.name for o in object_list), object_list)) 

def example_6(): 
    'izip, improved by John Fouhy' 
    d = dict(izip((o.name for o in object_list), object_list)) 

def example_7(): 
    'Provided by Jason Baker, removed brackets by John Fouhy' 
    d = dict((an_object.name, an_object) for an_object in object_list) 

timeits = [] 
for example_index in range(1, 8): 
    timeits.append(
     timeit.Timer(
      'example_%s()' % example_index, 
      'from __main__ import example_%s' % example_index) 
    ) 

for i in range(7): 
    timeit_object = timeits[i] 
    print 'Example #%s Result: "%s"' % (i+1, timeit_object.repeat(2)) 

Con 5 oggetti nella lista sto ottenendo un risultato di:

Example #1 Result: "[1.2428441047668457, 1.2431108951568604]" 
    Example #2 Result: "[3.3567759990692139, 3.3188660144805908]" 
    Example #3 Result: "[2.8346641063690186, 2.8344728946685791]" 
    Example #4 Result: "[3.0710639953613281, 3.0573830604553223]" 
    Example #5 Result: "[5.2079918384552002, 5.2170760631561279]" 
    Example #6 Result: "[3.240635871887207, 3.2402129173278809]" 
    Example #7 Result: "[3.0856869220733643, 3.0688989162445068]" 

e con il 50:

Example #1 Result: "[9.8108220100402832, 9.9066231250762939]" 
    Example #2 Result: "[16.365023136138916, 16.213981151580811]" 
    Example #3 Result: "[15.77024507522583, 15.771029949188232]" 
    Example #4 Result: "[14.598290920257568, 14.591825008392334]" 
    Example #5 Result: "[20.644147872924805, 20.64064884185791]" 
    Example #6 Result: "[15.210831165313721, 15.212569952011108]" 
    Example #7 Result: "[17.317100048065186, 17.359367847442627]" 

E, infine, con 500 oggetti:

Example #1 Result: "[96.682723999023438, 96.678673028945923]" 
    Example #2 Result: "[137.49416589736938, 137.48705387115479]" 
    Example #3 Result: "[136.58069896697998, 136.5823769569397]" 
    Example #4 Result: "[115.0344090461731, 115.1088011264801]" 
    Example #5 Result: "[165.08325910568237, 165.06769108772278]" 
    Example #6 Result: "[128.95187497138977, 128.96077489852905]" 
    Example #7 Result: "[155.70515990257263, 155.74126601219177]" 

Grazie a tutto ciò che ha risposto! Sono molto sorpreso del risultato. Se ci sono altri suggerimenti per un metodo più veloce mi piacerebbe sentirli. Ringrazia tutti!

+0

Dovresti anche controllare i suggerimenti qui, sono molto simili: http://stackoverflow.com/questions/753986/is-there-a-more-pythonic-way-to-build-this-dictionary/ 754154 # 754154 –

risposta

13

in Python 3.0 è possibile utilizzare una comprensione dict:

{an_object.name : an_object for an_object in object_list} 

Questo è possibile anche in Python 2, ma è un po 'più brutta:

dict([(an_object.name, an_object) for an_object in object_list]) 
+0

Ehi, bello, non sapevo che hanno aggiunto la comprensione del ditt nel 3.0 – hyperboreean

+3

Non c'è bisogno (in Python 2.4 o sopra) di creare una lista solo per buttarla via. Invece di dare al costruttore di dict una comprensione di lista, usa un'espressione generatore che itera solo una volta la sequenza: dict ((item.name, item) per item in object_list) – bignose

+0

@bignose - sì, sei corretto. Vedi la risposta di John Fouy per un modo per farlo. –

8
d = dict(zip([o.name for o in object_list], object_list)) 
+0

+1 Un buon modo per farlo in Python 2. –

+5

+1: Anche se si utilizza izip, si può risparmiare tempo e spazio, può essere importante se l'elenco è molto lungo. –

7

Se sei preoccupato con velocità, quindi possiamo migliorare leggermente le cose. La tua soluzione "verbosa" (che è davvero buona) non crea strutture dati intermedie. D'altra parte, la soluzione di hyperboreean,

d = dict(zip([o.name for o in object_list], object_list)) 

crea due liste inutili: [o.name for o in object_list] crea una lista, e zip(_, _) crea un altro elenco. Entrambe queste liste servono solo per essere ripetute una volta nella creazione del ditt.

Possiamo evitare la creazione di una lista sostituendo l'elenco comprensione con un generatore di espressione:

d = dict(zip((o.name for o in object_list), object_list)) 

Sostituzione zip con itertools.izip restituirà un iteratore ed evitare di creare la seconda lista:

import itertools 
d = dict(itertools.izip((o.name for o in object_list), object_list)) 

Possiamo modificare la soluzione di Jason Baker allo stesso modo, semplicemente eliminando le parentesi quadre:

d = dict((an_object.name, an_object) for an_object in object_list) 
+1

È vero, bello. – hyperboreean

Problemi correlati