Python fluent过滤器、地图等
Posted
技术标签:
【中文标题】Python fluent过滤器、地图等【英文标题】:Python fluent filter, map, etc 【发布时间】:2016-12-23 09:18:10 【问题描述】:我喜欢蟒蛇。但是,让我有点烦恼的一件事是,我不知道如何像 javascript 中的罐头那样以流畅的方式格式化功能活动。
例子(现场随机创建):能不能帮我把这个转换成python看起来流利的样子?
var even_set = [1,2,3,4,5]
.filter(function(x)return x%2 === 0;)
.map(function(x)
console.log(x); // prints it for fun
return x;
)
.reduce(function(num_set, val)
num_set[val] = true;
, );
我想知道是否有流动选项?也许是一个图书馆。
一般来说,我一直在对大多数事情使用列表推导,但如果我想打印,这是一个真正的问题
例如,如何使用列表理解在 python 2.x 中打印 1 - 5 之间的每个偶数(Python 3 print() 作为函数,但 Python 2 没有)。构造并返回列表也有点烦人。我宁愿只是 for 循环。
【问题讨论】:
只使用一个循环。函数式表示法会发出强烈的信号,表明您正在做一些没有副作用的事情,这是您不想为打印等操作发送的信号。 答案是不要使用理解来打印。理解是一种功能性结构,因此,当人们看到理解时,他们并不期望有副作用。 您遇到了麻烦,因为您提供的示例代码做了各种非功能性的事情 Python 用户不喜欢(或不关心)fluent interface。他们在任何地方都使用函数组合(将来还会更多)。他们声称流利的界面是非功能性的(我不同意这一点),所以要么忘记流利的界面,要么使用另一种适用于流利界面的语言。 Python 在这方面完全没有把握。我们中的许多人不得不使用这种语言来访问(伟大的)科学、统计和绘图库。语言本身从根本上被破坏了:它不支持集合操作的链接。 【参考方案1】:我现在正在寻找一个更接近问题核心的答案:
fluentpy
https://pypi.org/project/fluentpy/:
这是 streams
程序员(scala
、java
等)会喜欢的集合的方法链接:
import fluentpy as _
(
_(range(1,50+1))
.map(_.each * 4)
.filter(_.each <= 170)
.filter(lambda each: len(str(each))==2)
.filter(lambda each: each % 20 == 0)
.enumerate()
.map(lambda each: 'Result[%d]=%s' %(each[0],each[1]))
.join(',')
.print()
)
而且效果很好:
Result[0]=20,Result[1]=40,Result[2]=60,Result[3]=80
我现在正在尝试这个。如果它如上所示工作,今天将是非常美好的一天。
更新:看看这个:也许python
可以开始更合理的一行shell脚本:
python3 -m fluentpy "lib.sys.stdin.readlines().map(str.lower).map(print)"
这是它在命令行上的作用:
$echo -e "Hello World line1\nLine 2\Line 3\nGoodbye"
| python3 -m fluentpy "lib.sys.stdin.readlines().map(str.lower).map(print)"
hello world line1
line 2
line 3
goodbye
还有一个额外的 newline
应该清理掉 - 但它的要点很有用(无论如何对我来说)。
更新这是另一个库/选项:我改编自 gist
并在 pipy
上作为 infixpy
提供:
from infixpy import *
a = (Seq(range(1,51))
.map(lambda x: x * 4)
.filter(lambda x: x <= 170)
.filter(lambda x: len(str(x)) == 2)
.filter( lambda x: x % 20 ==0)
.enumerate() Ï
.map(lambda x: 'Result[%d]=%s' %(x[0],x[1]))
.mkstring(' .. '))
print(a)
【讨论】:
fluentpy 看起来是这里问题的最佳答案。我很惊讶它没有更受欢迎。【参考方案2】:生成器、迭代器和itertools
为链接和过滤操作提供了额外的功能。但与其记住(或查找)很少使用的东西,我更倾向于帮助函数和理解。
例如在这种情况下,使用辅助函数来处理日志记录:
def echo(x):
print(x)
return x
使用推导式的if
子句可以轻松选择偶数值。而且由于最终输出是字典,所以使用这种理解:
In [118]: d=echo(x):True for x in s if x%2==0
2
4
In [119]: d
Out[119]: 2: True, 4: True
或者要将这些值添加到现有字典中,请使用update
。
new_set.update(echo(x):True for x in s if x%2==0)
另一种编写方式是使用中间生成器:
y:True for y in (echo(x) for x in s if x%2==0)
或者在一个生成器中组合回声和过滤器
def even(s):
for x in s:
if x%2==0:
print(x)
yield(x)
后面是使用它的 dict comp:
y:True for y in even(s)
【讨论】:
虽然不是一个流畅的界面。这似乎是可用的最清晰的选项 函数式编程的 Python 操作指南docs.python.org/2/howto/functional.html【参考方案3】:理解是python处理filter/map操作的流畅方式。
您的代码将类似于:
def evenize(input_list):
return [x for x in input_list if x % 2 == 0]
理解不适用于控制台日志记录等副作用,因此请在单独的循环中进行。链接函数调用在 python 中并不是那么常见的习语。不要指望这会成为你的面包和黄油。 Python 库倾向于遵循“改变状态或返回值,但不是两者兼而有之”的模式。存在一些例外情况。
编辑:从好的方面来说,python 提供了多种类型的推导式,非常棒:
列表理解:[x for x in range(3)] == [0, 1, 2]
集合理解:x for x in range(3) == 0, 1, 2
字典理解:` x: x**2 for x in range(3) == 0: 0, 1: 1, 2: 4
生成器理解(或生成器表达式):(x for x in range(3)) == <generator object <genexpr> at 0x10fc7dfa0>
使用生成器理解,尚未评估任何内容,因此它是防止在大型集合上进行流水线操作时内存使用量激增的好方法。
例如,如果您尝试执行以下操作,即使使用 range
的 python3 语义:
for number in [x**2 for x in range(10000000000000000)]:
print(number)
在尝试构建初始列表时会出现内存错误。另一方面,将列表推导式更改为生成器推导式:
for number in (x**2 for x in range(1e20)):
print(number)
并且没有内存问题(它只需要永远运行)。发生的情况是构建了范围对象(它只存储了开始、停止和步长值(0、1e20 和 1))对象被构建,然后 for 循环开始迭代 genexp 对象。实际上,for循环调用
GENEXP_ITERATOR = `iter(genexp)`
number = next(GENEXP_ITERATOR)
# run the loop one time
number = next(GENEXP_ITERATOR)
# run the loop one time
# etc.
(请注意,GENEXP_ITERATOR 对象在代码级别是不可见的)
next(GENEXP_ITERATOR)
尝试从 genexp 中取出第一个值,然后开始迭代 range 对象,取出一个值,将其平方,然后将值作为第一个 number
。下一次 for 循环调用 next(GENEXP_ITERATOR)
时,生成器表达式从范围对象中提取第二个值,将其平方并生成它以用于 for 循环的第二次传递。第一组数字不再保存在内存中。
这意味着无论生成器理解中有多少项目,内存使用量都保持不变。您可以将生成器表达式传递给其他生成器表达式,并创建从不消耗大量内存的长管道。
def pipeline(filenames):
basepath = path.path('/usr/share/stories')
fullpaths = (basepath / fn for fn in filenames)
realfiles = (fn for fn in fullpaths if os.path.exists(fn))
openfiles = (open(fn) for fn in realfiles)
def read_and_close(file):
output = file.read(100)
file.close()
return output
prefixes = (read_and_close(file) for file in openfiles)
noncliches = (prefix for prefix in prefixes if not prefix.startswith('It was a dark and stormy night')
return prefix[:32]: prefix for prefix in prefixes
在任何时候,如果您需要某个数据结构,您可以将生成器推导传递给另一个推导类型(如本示例的最后一行),此时,它将强制生成器评估所有他们留下的数据,但除非你这样做,否则内存消耗将仅限于生成器单次传递中发生的情况。
【讨论】:
生成器已被提升为一种方式链接。 OP 以字典结尾,所以x: True for x in input_list if x % 2 == 0
可能更接近他/她的意图?
我实际上不知道你可以做“地图理解”@AlbertoGarcia-Raboso 这真的很有帮助!
@ThinkBonobo 它们实际上被称为dict理解。你也有列表和集合理解。它们是我最喜欢的 Python 特性之一。
我扩展了我的答案,讨论了 python 的理解类型以及它们是如何工作的。【参考方案4】:
您编写的代码最大的问题是 Python 不支持多行匿名函数。 filter
或 map
的返回值是一个列表,因此如果您愿意,可以继续链接它们。但是,您要么必须提前定义函数,要么使用 lambda。
【讨论】:
单表达式 lambdas 很划算。我很高兴他们不支持 OP 的代码。 +1【参考方案5】:尽管反对这样做的论据,这里是您的 JS 代码的 Python 翻译。
from __future__ import print_function
from functools import reduce
def print_and_return(x):
print(x)
return x
def isodd(x):
return x % 2 == 0
def add_to_dict(d, x):
d[x] = True
return d
even_set = list(reduce(add_to_dict,
map(print_and_return,
filter(isodd, [1, 2, 3, 4, 5])), ))
它应该适用于 Python 2 和 Python 3。
【讨论】:
我想在一天结束的时候,我希望能以一种流畅的方式来做你在最后一行所做的事情。甚至跳过打印并返回其中的一部分。如果它是像 '.filter(...).reduce(...)' 这样的东西,那就太好了,如果它不是 python 所做的,那就不是它所做的。我只是好奇 ..这表明python确实不以预期的流畅方式支持:需要提前定义函数是违反意义的【参考方案6】:有一个库已经完全符合您的要求,即流畅的语法、惰性求值和操作顺序与其编写方式相同,以及更多其他好东西,如多进程或多线程 Map/减少。
它被命名为pyxtension
,它已准备好并在PyPi 上维护。
您的代码将以这种形式重写:
from pyxtension.strams import stream
def console_log(x):
print(x)
return x
even_set = stream([1,2,3,4,5])\
.filter(lambda x:x%2 === 0)\
.map(console_log)\
.reduce(lambda num_set, val: num_set.__setitem__(val,True))
将map
替换为mpmap
用于多处理映射,或fastmap
用于多线程映射。
【讨论】:
谢谢,我想知道之前给出的 cmets,人们说流利的接口不是 Python 的。它还站着吗? 实际上链接是非常pythonic。以关于链接的 Guido van Rossum 邮件:mail.python.org/pipermail/python-dev/2003-October/038855.html 他反对排序返回自我,但链接是处理这样的字符串的基本 python 方式:y = x.rstrip("\n").split(":") .lower() 虽然,我不同意 Guido 关于 sort return self 的原因,读者不应该熟悉每种方法 - 否则如果您不熟悉方法,您打算如何阅读代码? 【参考方案7】:我们可以为此使用Pyterator(免责声明:我是作者)。
我们定义了打印和返回的函数(我相信你可以完全省略)。
def print_and_return(x):
print(x)
return x
然后
from pyterator import iterate
even_dict = (
iterate([1,2,3,4,5])
.filter(lambda x: x%2==0)
.map(print_and_return)
.map(lambda x: (x, True))
.to_dict()
)
# 2: True, 4: True
我已将您的 reduce 转换为可以转换为字典的元组序列。
【讨论】:
以上是关于Python fluent过滤器、地图等的主要内容,如果未能解决你的问题,请参考以下文章
NHibernate + Fluent NHibernate 异常
Fluent Nhibernate ApplyFilter on Join 列