六行代码实现 Python 管道

2021-01-10 17:14:24 +08:00
 abersheeran

不多说,直接上代码。

from functools import partial

class F(partial):
    def __ror__(self, other):
        if isinstance(other, tuple):
            return self(*other)
        return self(other)

使用样例。

range(10) | F(filter, lambda x: x % 2) | F(sum)

更详细的前因后果介绍,可以看 https://aber.sh/articles/Python-Pipe/ ,实在是懒得复制到 V2EX 调样式了。有兴趣的就看看,没兴趣的就复制代码去用就完事。

小小的得瑟一下,这是最近几天写的最满意、最有用的代码。妙手偶得之~

12429 次点击
所在节点    Python
122 条回复
abersheeran
2021-01-11 11:41:18 +08:00
@bruce00 谢谢~
abersheeran
2021-01-11 11:44:28 +08:00
@Wincer 要实现你这个,首先要让这些 callable 支持自动柯里化……不是支持一个管道运算符就行了的

自动柯里化的工作量就太大了,因为 Py 里存在 *args 和 **kwargs 。我是想不到有什么好办法能实现的。所以我选择让程序员自己柯里化一次(也就是使用 F 包裹一次)。
Wincer
2021-01-11 11:46:59 +08:00
@abersheeran 嗯是的,这确实是目前比较合适的方法了
iqxd
2021-01-11 11:48:01 +08:00
请问下楼主 if isinstance(other, tuple): return self(*other) 是为了处理哪种情况呢?
abersheeran
2021-01-11 11:53:13 +08:00
@frostming
@Wincer

就比如说明希想要的那个,可以用
def fp(func):
def _(*args, **kwargs):
return F(func, *args, **kwargs)
return _

折中实现一下,强制 double call 才能实际调用。

而你要做的这个,既要支持 direct call 又要支持
double call……那就得第一次调用之后延迟计算,判断如果是走管道,调第二次,判断直接赋值的话再去计算。CPython 应该做不到,可以考虑做个超集语言,在编译期做。
NeezerGu
2021-01-11 12:03:31 +08:00
@abersheeran
不是这个吗? https://github.com/python/cpython/blob/master/Lib/functools.py
哦,这是说, 同时提供了 python 版本和 c 版本? 如果 c 版本的导入失败就用 python 的?
frostming
2021-01-11 12:38:10 +08:00
@Wincer 如果只是 patch function 大可直接把 builtin 的 map, filter 换掉,我说的那种可以用来给 list 加 chaining call:

[1, 2, 3].map(lambda x: x**2)
mckelvin
2021-01-11 13:01:55 +08:00
`sum(i for i in range(10) if i % 2)` 比 `range(10) | F(filter, lambda x: x % 2) | F(sum)` 更难读吗?
yuruizhe
2021-01-11 13:04:14 +08:00
@Leigg 同+1,我还纳闷 shell 咋能调用 python 函数呢…老实说,我还真没把 python shell 当成过主力 shell,都是 bash…
muzuiget
2021-01-11 13:10:53 +08:00
这种用法容易走火入魔。
Merlini
2021-01-11 13:14:02 +08:00
之前做文本处理的时候看到网上一个 pipeline 写法感觉也挺不错。
```python
def pipeline(
value: T,
function_pipeline: Sequence[Callable[[T], T]],
) -> T:
"""A generic Unix-like pipeline
:param value: the value you want to pass through a pipeline
:param function_pipeline: an ordered list of functions that
comprise your pipeline
"""
return reduce(lambda v, f: f(v), function_pipeline, value)
```
zouzou0208
2021-01-11 13:25:22 +08:00
好玩,原来是 index.py 的作者。好棒。
chaleaoch
2021-01-11 13:29:09 +08:00
在这么玩, 会被玩坏的喂~~~~~
shyling
2021-01-11 13:43:09 +08:00
🤪f#自带语法
Wincer
2021-01-11 14:23:56 +08:00
@frostming 是的,这样也可以,但是适用性会窄一些,需要预先针对某一种类型 patch
shyrock
2021-01-11 14:42:38 +08:00
赞!学习了。
aldslvda
2021-01-11 14:52:48 +08:00
学习了
iintothewind
2021-01-11 16:07:57 +08:00
你这个管道实现是不是可以再优化一下,
毕竟每次都得带 F() 包住后面的操作符, 这个看起来有点多余。
admirez
2021-01-11 16:11:52 +08:00
pandas 也可以这样搞么?
sapocaly
2021-01-11 16:50:00 +08:00
个人觉得 chain funciton call 可能更符合 python 一些至少更接近 django 一些。比如 range(10).filter(lambda x: x % 2).sum()。毕竟每次加个 F 还是有点难受。当然,去掉 F 容易,我是没想到怎么实现 range(10) | filter, lambada x:x |sum 这样的 syntax

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/743574

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX