Подтвердить что ты не робот

Понимание списков Python против .NET LINQ

Следующий простой код LINQ

string[] words = { "hello", "wonderful", "linq", "beautiful", "world" };

// Get only short words
var shortWords =
  from word in words
  where word.Length <= 5
  select word;

// Print each word out
shortWords.Dump();

может быть переведен на python с использованием понимания списка следующим образом.

words = ["hello", "wonderful", "linq", "beautiful", "world"]
shortWords = [x for x in words if len(x) <=5]
print shortWords
  • Является ли LINQ еще одной идеей для реализации понимания списка?
  • Какие примеры могут быть в том, что LINQ может выполнять, но переосмысление списка не может быть выполнено.
4b9b3361

Ответ 1

(Предупреждение: мамонт отвечает вперед. Часть до первой горизонтальной линии делает хороший раздел tl; dr, я полагаю)

Я не уверен, что я отношусь к гуру Python... но у меня есть четкое понимание итерации на Python, поэтому попробуйте:)

Во-первых: Afaik, запросы LINQ выполняются лениво - если в этом случае выражения генератора являются более близкой концепцией Python (в любом случае, представления list-, dict- и set понимаются концептуально просто выражения генератора, подаваемые в список /dict/set constructor!).

Кроме того, существует концептуальная разница: LINQ - это, как говорится в названии, запрос структур данных. List-/dict-/set assrehensions - это возможное применение этого (например, фильтрация и проецирование элементов списка). Поэтому они на самом деле менее общие (как мы увидим, многие вещи, встроенные в LINQ, не встроены в них). Подобным образом, выражения генератора - это способ сформулировать одноразовый итератор вперед на месте (мне нравится думать об этом как лямбда для функций генератора, только без уродливого длинного ключевого слова;)), а не для описания сложного запроса, Они перекрываются, да, но они не идентичны. Если вы хотите использовать все возможности LINQ в Python, вам нужно будет написать полноценный генератор. Или объедините многочисленные мощные генераторы, встроенные и в itertools.


Теперь, аналоги Python для возможностей LINQ Джон Скит назвал:

Проецирование: (x.foo for ...)

Фильтрация: (... if x.bar > 5)

  • Объединение (x join 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), я полагаю.

Обратите внимание, что это не будет перебирать по всему y для каждого x_item, оно получит только первое совпадение.

  • Групповое объединение (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 с соответствующими значениями. Правильная вещь обычно подает результаты конструктору класса approriate, скажем, 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, чтобы получить что-то, что мы можем индексировать и повторять несколько раз). Но кто знает, нужны ли нам групповые значения...

  • Заказ (orderby x.foo по возрастанию, y.bar по убыванию)

Для сортировки нужен специальный синтаксис? Встроенный sorted работает и для итераций: sorted(x % 2 for x in range(10)) или sorted(x for x in xs, key=attrgetter('foo')). Сортированный по возрастанию по умолчанию аргумент ключевого слова reverse дает убывающий порядок.

Увы, сортировка afaik по нескольким атрибутам не так проста, особенно при смешении восходящего и нисходящего. Хм... тема для рецепта?

  • Промежуточные переменные (пусть 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 определенно не делает этого. Выражения списка соответствуют взаимно однозначным для накопления простого списка в (возможно, вложенном) для цикла, генераторные выражения соответствуют друг другу одному генератору. Учитывая модуль parser и ast, теоретически можно было бы написать библиотеку для преобразования понимания в, например, SQL-запрос. Но никто не заботится.

Ответ 2

Ну, вам нужно различать разные вещи:

  • Стандартные операторы запроса LINQ
  • выражения запроса LINQ в С#
  • выражения запроса LINQ в VB

С# не поддерживает столько запросов запроса, сколько VB, но вот что он поддерживает:

  • Проецирование (select x.foo)
  • Фильтрация (where x.bar > 5)
  • Объединение (x join y on x.foo equals y.bar)
  • Групповое объединение (x join y on x.foo equals y.bar into g)
  • Группирование (group x.foo by x.bar)
  • Заказ (orderby x.foo ascending, y.bar descending)
  • Промежуточные переменные (let tmp = x.foo)
  • Сглаживание (from x in y from z in x)

Я не знаю, сколько из них поддерживается непосредственно в представлениях списка Python.

Обратите внимание, что хотя LINQ to Objects имеет дело с делегатами, другие поставщики запросов (например, LINQ to SQL) могут обрабатывать деревья выражений, которые описывают запрос, а не просто представляют исполняемые делегаты. Это позволяет преобразовать запрос в SQL (или другие языки запросов) - опять же, я не знаю, поддерживает ли Python такую ​​вещь или нет. Это значительная часть LINQ, хотя.

Ответ 3

Используя пакет asq Python, вы можете легко сделать большинство вещей на Python, что вы можете сделать на С#, используя LINQ-for-objects, Используя asq, ваш пример Python будет выглядеть следующим образом:

from asq.initiators import query
words = ["hello", "wonderful", "linq", "beautiful", "world"]
shortWords = query(words).where(lambda x: len(x) <= 5)

Ответ 4

Я не гуру Python, но я бы сказал, что Python фактически поддерживает все из них, поскольку вы можете вставлять списки и включать все необходимые лямбда-выражения. (понимание списков, как правило, трудно читать, если они становятся слишком сложными, хотя...), но нет, он не содержит "конкретного синтаксиса" для выполнения всего этого.

Большинство функций можно воспроизвести, используя:  - распознавания списков или генераторов  - лямбда-функции или встроенные функции (например, filter() или map()) или функции из модуля itertools

Например, если вы хотите скопировать поведение:

  • Проекции: это будет левая часть понимания списка... которые могут быть одиночными значениями, а также кортежей. ex: [ (k,v) for k,v in my_dict.items() if k.startswith("abc"]. Вы также можете использовать map()
  • Фильтрация: это будет выражение справа, после if. Вы также можете использовать filter()
  • Заказ: просто используйте встроенный sorted()
  • Группирование или агрегатов: используйте встроенные min(), max() или itertools.groupby()

Что касается присоединяется или сглаживания, я думаю, вам придется "сделать это вручную"...

(Всегда полезно иметь краткую справочную информацию по Python в пределах досягаемости)