Слева направо применение операций над списком в 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.
После довольно тщательного обзора имеющихсясторонних библиотек представляется, что
Pipe
https://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)