2012-02-28 15 views
49

estoy usando ORM de SQL Alquimia y me encuentro cuando vuelva una sola columna que tenga los resultados, así:SQL Alchemy ORM devolver una sola columna, la forma de evitar el procesamiento posterior común

[(result,), (result_2,)] # etc... 

con un conjunto como esto me parece que tengo que hacer esto a menudo:

results = [r[0] for r in results] # So that I just have a list of result values 

esto no es tan "malo" porque mis conjuntos de resultados suelen ser pequeñas, pero si no eran esto podría agregar una sobrecarga significativa. Lo más importante es que siento que desordena la fuente, y perder este paso es un error bastante común que me encuentro.

¿Hay alguna forma de evitar este paso adicional?

Un relaciona a un lado: Este comportamiento de la ORM parece inconveniente en este caso, pero otro caso en el que mi conjunto de resultados era, [(id, valor)] que termina así:

[(result_1_id, result_1_val), (result_2_id, result_2_val)] 

que entonces puede hacerlo:

results = dict(results) # so I have a map of id to value 

Tiene la ventaja de tener sentido como un paso útil después de devolver los resultados.

Lo que tienes es un problema o estoy simplemente ser un nitpick y el procesamiento posterior después de obtener el conjunto de resultados tiene sentido para ambos casos? Estoy seguro de que podemos pensar en algunas otras operaciones comunes de procesamiento posterior para hacer que el conjunto de resultados sea más útil en el código de la aplicación. ¿Existe un alto rendimiento y soluciones convenientes en todos los ámbitos o es inevitable el procesamiento posterior, y simplemente se requiere para diferentes usos de aplicaciones?

Cuando mi aplicación puede aprovechar los objetos devueltos por SQL Alchemy's ORM parece extremadamente útil, pero en los casos en que no puedo o no puedo, no tanto. ¿Es este solo un problema común de los ORM en general? ¿Será mejor que no use la capa ORM en casos como este?

supongo que debería mostrar un ejemplo de las consultas ORM reales que estoy hablando:

session.query(OrmObj.column_name).all() 

o

session.query(OrmObj.id_column_name, OrmObj.value_column_name).all() 

Por supuesto, en una consulta real hay normalmente habría alguna filtros, etc.

Respuesta

19

postal de Python combinado con el operador * de expansión en línea es una solución bastante práctico para esto:

>>> results = [('result',), ('result_2',), ('result_3',)] 
>>> zip(*results) 
[('result', 'result_2', 'result_3')] 

Entonces sólo tiene que [0] Índice de una vez. Para obtener una lista con tan poco tiempo de su comprensión es más rápido:

>>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000) 
0.010490894317626953 
>>> timeit('result = [ result[0] for result in [("result",), ("result_2",), ("result_3",)] ]', number=10000) 
0.0028390884399414062 

Sin embargo para las listas más largas zip debe ser más rápido:

>>> timeit('result = zip(*[(1,)]*100)', number=10000) 
0.049577951431274414 
>>> timeit('result = [ result[0] for result in [(1,)]*100 ]', number=10000) 
0.11178708076477051 

por lo que toca a usted para determinar lo que es mejor para su situación.

10

Una forma de reducir el desorden en la fuente es iterar así:

results = [r for (r,) in results] 

Aunque esta solución es un personaje más que si utiliza el operador [], creo que es más fácil en los ojos.

Para obtener menos desorden, quite el paréntesis. Esto hace más difícil cuando se lee el código, darse cuenta de que en realidad estás manejo de tuplas, sin embargo:

results = [r for r, in results] 
8

tuve problemas con esto también hasta que me di cuenta de que es igual que cualquier otra consulta:

for result in results: 
    print result.column_name 
+0

Sí, estoy de acuerdo, las NamedTuples no son inmediatamente obvias. Mi proceso de publicación más común todavía tiene que crear un diccionario de algún tipo. La mayor parte de mi postprocesamiento se eliminó mediante una mejor arquitectura de la base de datos y el uso de SQLAlchemy, así que tengo los valores que necesito adjuntar a los objetos ORM, o disponibles a través de los métodos. –

0

Mi solución se ve así;)

def column(self): 
    for column, *_ in Model.query.with_entities(Model.column).all(): 
     yield column 

NOTA: solo py3.

1

I encontraron la siguiente más legible, también incluye la respuesta para el dict (en Python 2.7):

d = {id_: name for id_, name in session.query(Customer.id, Customer.name).all()} 
l = [r.id for r in session.query(Customer).all()] 

Para el valor individual, préstamos de otra respuesta:

l = [name for (name,) in session.query(Customer.name).all()] 

Comparar con la solución zip incorporado, adaptado a la lista:

l = list(zip(*session.query(Customer.id).all())[0]) 

que en mi timeits proporciona solo un 4% de mejoras de velocidad.

0

Wow, chicos, ¿por qué se tensan? Hay método más empinado camino, más rápido y más elegante)

>>> results = [('result',), ('result_2',), ('result_3',)] 
>>> sum(results, tuple()) 
('result', 'result_2', 'result_3') 

Velocidad:

>>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000) 
0.004222994000883773 
>>> timeit('result = sum([("result",), ("result_2",), ("result_3",)],())', number=10000) 
0.0038205889868550003 

Pero si hay más elementos en la lista - sólo utilizan postal. Zip más velocidad.

Cuestiones relacionadas