2009-04-26 14 views
6

Estoy intentando tomar una lista de objetos y convertir esa lista en un dict. Los valores dict serían cada objeto en la lista, y las claves dict serían un valor encontrado en cada objeto.¿La mejor manera de convertir una lista en un dict, donde las claves son un valor de cada objeto?

Aquí hay un código que representa lo que estoy haciendo:

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 

ahora trabaja ese código, pero es un poco feo, y un poco lento. ¿Alguien podría dar un ejemplo de algo más rápido/"mejor"?

editar: Muy bien, gracias por las respuestas. Debo decir que estoy sorprendido de ver las formas más pitónicas que parecen más lentas que las hechas a mano.

Edit2: Muy bien, he actualizado el código de prueba para que sea un poco más fácil de leer, con tantas pruebas je.

Aquí es donde estamos en términos de código, puse autores en el código y si me equivoqué, por favor hágamelo saber.

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 objetos de la lista que estoy recibiendo un resultado de:

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]" 

y con 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]" 

Y, por último, con 500 objetos:

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]" 

¡Gracias a todos los que respondieron! Estoy muy sorprendido con el resultado. Si hay otros consejos para un método más rápido, me encantaría escucharlos. ¡Gracias a todos!

+0

También debería ver las sugerencias aquí, son muy similares: http://stackoverflow.com/questions/753986/is-there-a-more-pythonic-way-to-build-this-dictionary/ 754154 # 754154 –

Respuesta

13

en Python 3.0 se puede utilizar una comprensión dict:

{an_object.name : an_object for an_object in object_list} 

Esto también es posible en Python 2, pero es un poco más feo:

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

Oye, qué bueno, no sabía que agregaron la comprensión dictada en 3.0 – hyperboreean

+3

No es necesario (en Python 2.4 o superior) crear una lista solo para descartarla. En lugar de darle al constructor dict una lista de comprensión, use una expresión del generador que solo repite la secuencia una vez: dict ((item.name, item) para item en object_list) – bignose

+0

@bignose - si, está en lo correcto. Vea la respuesta de John Fouy para saber cómo hacerlo. –

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

+1 Una buena manera de hacer esto en Python 2. –

+5

+1: Además, si usa izip puede ahorrar tiempo y espacio, puede ser importante si la lista es muy larga. –

7

Si le preocupa con la velocidad, entonces podemos mejorar las cosas un poco. Su solución "verbosa" (que es realmente buena) no crea estructuras de datos intermedias. Por otra parte, la solución de hyperboreean,

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

crea dos listas innecesarios: [o.name for o in object_list] crea una lista, y zip(_, _) crea otra lista. Ambas listas solo sirven para ser repetidas una vez en la creación del dict.

Podemos evitar la creación de una lista mediante la sustitución de la lista por comprensión con una expresión generadora:

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

Sustitución zip con itertools.izip devolverá un iterador y evitar la creación de la segunda lista:

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

Podríamos modificar la solución de Jason Baker de la misma manera, simplemente borrando los corchetes:

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

Eso es cierto, bueno. – hyperboreean

Cuestiones relacionadas