SQL Алхимия ORM возвращает один столбец, как избежать обычной постобработки - PullRequest
67 голосов
/ 28 февраля 2012

Я использую ORM в SQL Alchemy и нахожу, что, возвращая один столбец, я получаю результаты примерно так:

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

С таким набором я нахожу, что мне приходится делать это часто:

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

Это не так уж и плохо, потому что мои наборы результатов, как правило, маленькие, но если бы их не было, это могло бы привести к значительным накладным расходам. Самое главное, я чувствую, что это загромождает источник, и пропустить этот шаг - довольно распространенная ошибка, с которой я сталкиваюсь.

Есть ли способ избежать этого дополнительного шага?

Смежный в стороне: это поведение orm кажется неудобным в этом случае, но в другом случае, когда мой набор результатов был, [(id, value)], это заканчивается так:

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

Я тогда могу просто сделать:

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

Преимущество этого подхода состоит в том, что он полезен как шаг после возврата результатов.

Это действительно проблема, или я просто придираюсь, и постобработка после получения набора результатов имеет смысл для обоих случаев? Я уверен, что мы можем подумать о некоторых других распространенных операциях постобработки, чтобы сделать набор результатов более удобным в коде приложения. Существуют ли высокопроизводительные и удобные решения по всем направлениям или постобработка неизбежна и просто необходима для различных применений приложений?

Когда мое приложение может на самом деле воспользоваться объектами, возвращаемыми ORM SQL Alchemy, это кажется чрезвычайно полезным, но в случаях, когда я не могу или не могу, не так много. Это просто общая проблема ORM в целом? Мне лучше не использовать слой ORM в подобных случаях?

Полагаю, мне следует показать пример реальных запросов orm, о которых я говорю:

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

или

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

Конечно, в реальном запросе обычно есть несколько фильтров и т. Д.

Ответы [ 6 ]

22 голосов
/ 04 октября 2012

Почтовый индекс Python в сочетании с оператором * встроенного расширения является довольно удобным решением для этого:

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

Тогда вам нужно только [0] индексировать один раз.Для такого короткого списка ваше понимание быстрее:

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

Однако для более длинных списков zip должен быть быстрее:

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

Так что вам решать, что лучше для вашей ситуации.

18 голосов
/ 29 октября 2012

Один из способов уменьшить беспорядок в источнике - это выполнить итерацию следующим образом:

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

Хотя это решение на один символ длиннее, чем использование оператора [], я думаю, что это проще для глаз.

Для еще меньшего количества беспорядка удалите скобки.Это затрудняет чтение кода, когда вы замечаете, что вы действительно работаете с кортежами:

results = [r for r, in results]
11 голосов
/ 26 апреля 2013

Я тоже боролся с этим, пока не понял, что это так же, как и любой другой запрос:

for result in results:
     print result.column_name
2 голосов
/ 01 января 2017

Я нашел следующее более читабельным , также включает ответ для dict (в 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()]

Для единственного значения, заимствованного из другого ответа:

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

Сравните со встроенным решением zip, адаптированным к списку:

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

, которое в мое время обеспечивает только около 4% улучшения скорости.

0 голосов
/ 26 февраля 2018

Ух ты, ребята, зачем напрягаться? Есть способ круче, быстрее и элегантнее)

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

Скорость:

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

Но если в списке больше элементов - используйте только zip . Молния больше скорости.

0 голосов
/ 17 ноября 2015

Мое решение выглядит так;)

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

ПРИМЕЧАНИЕ: только py3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...