Слева направо применение операций над списком в python3
Существует ли какой-либо возможный способ добиться не-ленивого вызова слева направо операций над списком в python ?
Например scala
val a = ((1 to 50)
.map(_ * 4)
.filter( _ <= 170)
.filter(_.toString.length == 2)
.filter (_ % 20 == 0)
.zipWithIndex
.map{ case(x,n) => s"Result[$n]=$x"}
.mkString(" .. "))
a: String = Result[0]=20 .. Result[1]=40 .. Result[2]=60 .. Result[3]=80
Хотя я понимаю, что многие люди не предпочтут вышеприведенный синтаксис, мне нравится возможность двигаться слева направо и добавлять произвольные операции по ходу.
Понимание python for Имо не легко читать, когда есть три или более операций. В результате, кажется, мы должны разбить все на куски.
[f(a) for a in g(b) for b in h(c) for ..]
Есть ли какой-либо шанс для упомянутого подхода?
Примечание: я опробовал несколько библиотек, включая toolz.functoolz. Это усложняется тем, что python3 ленивая оценка: каждый уровень возвращает объект map. Кроме того, не очевидно, что он может работать на входе list .2 ответа:
Ответ от @JohanL делает хорошую работу, чтобы увидеть, что ближайший эквивалент находится в стандартных библиотеках python.
После довольно тщательного обзора имеющихсясторонних библиотек представляется, что
Pipehttps://github.com/JulienPalard/Pipe лучше всего соответствует потребностям .Обновить следующим образом: это круто. Вы можете создать свои собственные функции конвейера. Я поставил его на работу для пререканий с каким-то текстом. под. жирная линия - это место, где происходит работа. Все эти
Задача здесь состоит в том, чтобы связать аббревиатуру в первом тексте:@Pipeвещи я должен кодировать только один раз, а затем могу использовать повторно.rawLabels="""Country: Name of country Agr: Percentage employed in agriculture Min: Percentage employed in mining Man: Percentage employed in manufacturing PS: Percentage employed in power supply industries Con: Percentage employed in construction SI: Percentage employed in service industries Fin: Percentage employed in finance SPS: Percentage employed in social and personal services TC: Percentage employed in transport and communications"""С соответствующим тегом в этом втором тексте:
mylabs = "Country Agriculture Mining Manufacturing Power Construction Service Finance Social Transport"Вот одноразовое кодирование для функциональных операций (повторное использование в последующих конвейерах):
@Pipe def split(iterable, delim= ' '): for s in iterable: yield s.split(delim) @Pipe def trim(iterable): for s in iterable: yield s.strip() @Pipe def pzip(iterable,coll): for s in zip(list(iterable),coll): yield s @Pipe def slice(iterable, dim): if len(dim)==1: for x in iterable: yield x[dim[0]] elif len(dim)==2: for x in iterable: for y in x[dim[0]]: yield y[dim[1]] @Pipe def toMap(iterable): return dict(list(iterable))И вот большой финал: все в одном конвейере:
labels = (rawLabels.split('\n') | trim | split(':') | slice([0]) | pzip(mylabs.split(' ')) | toMap )И результат:
print('labels=%s' % repr(labels)) labels={'PS': 'Power', 'Min': 'Mining', 'Country': 'Country', 'SPS': 'Social', 'TC': 'Transport', 'SI': 'Service', 'Con': 'Construction', 'Fin': 'Finance', 'Agr': 'Agriculture', 'Man': 'Manufacturing'}
Несмотря на то, что он не считается Питоническим, Python все еще содержит
mapиfilterиreduceмогут быть импортированы изfunctools. Используя эти функции, можно сгенерировать тот же трубопровод, что и в scala, хотя он будет записан в противоположном направлении (справа налево, а не слева направо):from functools import reduce a = reduce(lambda f,s: f'{f} .. {s}', map(lambda nx: f'Result[{nx[0]}]: {nx[1]}', enumerate( filter(lambda n: n%20 == 0, filter(lambda n: len(str(n)) == 2, filter(lambda n: n <= 170, map(lambda n: n*4, range(1,51))))))))Теперь, это лениво, в том смысле, что он позволит каждому значению транспортироваться по всей трубе, прежде чем будет оценено следующее. Впрочем, так как все значения потребляются конечным вызовом
reduce, этого не видно.Можно генерировать список из каждого объекта
mapилиfilterна каждом шаге:Оба эти выражения, особенно второе, довольно многословны, поэтому я не знаю, Рекомендую ли я их. Я бы рекомендовал использовать список / генератор понимания и несколько промежуточных varaiables:a = reduce(lambda f,s: f'{f} .. {s}', list(map(lambda nx: f'Result[{nx[0]}]: {nx[1]}', list(enumerate( list(filter(lambda n: n%20 == 0, list(filter(lambda n: len(str(n)) == 2, list(filter(lambda n: n <= 170, list(map(lambda n: n*4, list(range(1,51)))))))))))))))n4 = [n*4 for n in range(1,51)] fn4 = [n for n in n4 if n <= 170 if len(str(n))==2 if n%20 == 0] rfn4 = [f'Result[{n}]: {x}' for n, x in enumerate(fn4)] a = ' .. '.join(rfn4)Еще одно преимущество такого подхода (по крайней мере, для вас) заключается в том, что при таком подходе вы будете сохранять порядок операций, который находится в scala. Кроме того, до тех пор, пока мы не перечислим понимание (как показано), оно будет оцениваться не лениво. Если мы хотим ленивой оценки, то вместо этого можно сделать генераторное понимание:
Таким образом, единственная разница заключается в том, что мы используем парантезы вместо скобок. Но, как было сказано ранее, поскольку все данные потребляются, разница в этом примере довольно минимальна.n4 = (n*4 for n in range(1,51)) fn4 = (n for n in n4 if n <= 170 if len(str(n))==2 if n%20 == 0) rfn4 = (f'Result[{n}]: {x}' for n, x in enumerate(fn4)) a = ' .. '.join(rfn4)
