(Внимание: ответ мамонта впереди. Часть до первой горизонтальной линии дает хороший tl; секция dr, я полагаю)
Я не уверен, что я квалифицируюсь как гуру Python ... но я хорошо разбираюсь в итерациях в Python, поэтому давайте попробуем:)
Прежде всего: Afaik, запросы LINQ выполняются лениво - в этом случае выражения-генераторы являются более близким понятием Python (в любом случае, понимания списков, диктов и множеств концептуально являются выражениями-генераторами, подаваемыми в список / dict / установить конструктор!).
Кроме того, есть концептуальное отличие: LINQ предназначен, как следует из названия, для запроса структур данных. Возможны применения этого в качестве списков / диктовок / наборов (например, фильтрация и проецирование элементов списка). Таким образом, они на самом деле менее общие (как мы увидим, многие вещи, встроенные в LINQ, не встроены в них). Аналогично, выражения генератора - это способ сформулировать одноразовый прямой итератор на месте (мне нравится думать о нем как о лямбде для функций генератора, только без уродливого, длинного ключевого слова;)), а не способ описать сложный запрос. , Да, они совпадают, но они не идентичны. Если вам нужна вся мощь LINQ в Python, вам придется написать полноценный генератор. Или объединить многочисленные мощные генераторы встроенные и в itertools
.
Теперь коллеги из Python по возможностям LINQ Джон Скит назвал:
Прогнозы: (x.foo for ...)
Фильтрация: (... if x.bar > 5)
- Joins (x присоединиться к y на x.foo равно y.bar)
Ближайшая вещь была бы ((x_item, next(y_item for y_item in y if x_item.foo == y_item.bar)) for x_item in x)
, я полагаю.
Обратите внимание, что для каждого x_item это не будет выполнять итерацию по всему y, а будет только первое совпадение.
- Групповые объединения (x join y на x.foo равен y.bar в g)
Это сложнее. В Python нет анонимных типов, хотя они тривиальны, если вы не против возиться с __dict__
:
.
class Anonymous(object):
def __init__(self, **kwargs):
self.__dict__ = kwargs
Тогда мы могли бы сделать (Anonymous(x=x, y=y) for ...)
, чтобы получить список объектов, которые имеют x
и y
членов с соответствующими значениями.
Правильно обычно передавать результаты конструктору класса, скажем, XY.
- Группировка (группа x.foo по x.bar)
Теперь это становится волосатым ... нет встроенного способа, афаик. Но мы можем определить это сами, если нам это нужно:
from collections import defaultdict
def group_by(iterable, group_func):
groups = defaultdict(list)
for item in iterable:
groups[group_func(item)].append(item)
return groups
Пример: * * тысяча пятьдесят-четырь
>>> from operator import attrgetter
>>> group_by((x.foo for x in ...), attrgetter('bar'))
defaultdict(<class 'list'>, {some_value_of_bar: [x.foo of all x where x.bar == some_value_of_bar], some_other_value_of_bar: [...], ...})
Это требует, чтобы все, что мы группируем, было хэшируемым. Этого можно избежать, и я сделаю удар, если будет общественный спрос. Но сейчас я ленивый:)
Мы также можем просто вернуть итерируемую группу без значений, которые мы сгруппировали, вызвав .values()
результат (конечно, мы можем передать , что , list
, чтобы получить то, что мы можем индексировать и повторить несколько раз). Но кто знает, не понадобятся ли нам групповые значения ...
- Порядок (по возрастанию x.foo, по убыванию y.bar)
Для сортировки нужен специальный синтаксис? Встроенный sorted
работает и для итераций: sorted(x % 2 for x in range(10))
или sorted(x for x in xs, key=attrgetter('foo'))
. По умолчанию сортируется по возрастанию, ключевое слово аргумента reverse
дает порядок убывания.
Увы, афаик сортировка по нескольким атрибутам не так проста, особенно при смешивании по возрастанию и по убыванию. Хм ... тема для рецепта?
- Промежуточные переменные (пусть tmp = x.foo)
Нет, невозможно в выражениях или в выражениях-генераторах - они, как следует из названия, должны быть выражениями (и обычно занимают только одну или две строки). Это вполне возможно в функции генератора, хотя:
(x * 2 for x in iterable)
переписано как генератор с промежуточной переменной:
def doubles(iterable):
for x in iterable:
times2 = x * 2
yield times2
Сглаживание: (c for s in ("aa","bb") for c in s )
Обратите внимание: хотя LINQ to Objects работает с делегатами, другие поставщики запросов (например, LINQ to SQL) могут работать с деревьями выражений, которые описывают запрос, а не просто представляют исполняемые делегаты.Это позволяет переводить запрос в SQL (или другие языки запросов) - опять же, я не знаю, поддерживает ли Python подобные вещи или нет.Это важная часть LINQ.
Python определенно не делает ничего подобного.Выражения списка соответствуют взаимно однозначному накоплению простого списка в (возможно, вложенном) цикле for, выражения генератора соответствуют однозначному генератору.Учитывая parser
и ast
модуль, было бы теоретически написать библиотеку для преобразования понимания, например, в SQL-запрос.Но никому нет до этого дела.