V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
abersheeran
V2EX  ›  Python

六行代码实现 Python 管道

  •  7
     
  •   abersheeran ·
    abersheeran · 2021-01-10 17:14:24 +08:00 · 12342 次点击
    这是一个创建于 1202 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不多说,直接上代码。

    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 调样式了。有兴趣的就看看,没兴趣的就复制代码去用就完事。

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

    第 1 条附言  ·  2021-01-12 15:19:15 +08:00

    应某位回复者的要求,已经发布到 PyPi,顺便在 GitHub 建了个库,写了使用说明。https://github.com/abersheeran/only-pipe

    稍微解决了一下好几个人觉得困扰的 tuple 判断。现在在库里,F 只会传一个参数,如果你想让 tuple 被拆解,应当使用 FF。是否解包,控制权交给程序员,而不是代码本身。

    增加了一个功能,可以把 Freduce 释放到全局,就像 map/filter 一样直接用就行。

    import pipe
    import functools
    
    pipe.set_global(pipe.F, pipe.FF, functools.reduce)
    
    assert range(10) | F(filter, lambda x: x % 2) | F(sum) == 25
    assert (1, 2, 3) | F(sum) == 6
    
    assert (1, 2) | FF(lambda x, y: x + y) == 3
    
    assert range(10) | F(reduce, lambda x, y: x + y) == 45
    
    第 2 条附言  ·  2021-01-12 16:48:57 +08:00

    有回复者提到的 Filter = F(filter) 的类似写法,可通过手动柯里化实现。

    from functools import reduce
    from pipe import F
    
    
    def fp(func):
        def _(*args, **kwargs):
            return F(func, *args, **kwargs)
        return _
    
    
    Filter = fp(filter)
    Map = fp(map)
    Reduce = fp(reduce)
    
    range(100) | Filter(lambda x: x % 2) | Map(lambda x: x * x) | Reduce(lambda x, y: x + y)
    

    https://github.com/abersheeran/only-pipe/discussions/1

    122 条回复    2021-02-19 02:25:18 +08:00
    1  2  
    no1xsyzy
        101
    no1xsyzy  
       2021-01-12 23:58:50 +08:00
    @yueyoum 你先做个能够同时符合你这 6 条里两条的语言出来吧。
    这六条,两两冲突……
    black11black
        102
    black11black  
       2021-01-13 09:12:26 +08:00
    震惊于还有这种写法

    看了楼上的讨论,对我个人而言感觉最合适的语法是

    ```
    a = PIPE | range(10) | (map , str) | END
    ```

    这种格式,有初始化时不需要显式新建对象,感觉有时间可以封装一下
    black11black
        103
    black11black  
       2021-01-13 09:15:25 +08:00
    我觉得直接封装函数无意义,比如 PIPE 并不直接返回结果而是创建一个可供输入的对象,我并不常用这种功能。如果有需要我更愿意另外显式封装函数
    abersheeran
        104
    abersheeran  
    OP
       2021-01-13 09:18:56 +08:00
    @black11black 如果是这么用呢?

    range(100) | Filter(lambda x: x % 2) | Map(lambda x: x * x) | Reduce(lambda x, y: x + y)

    https://github.com/abersheeran/only-pipe/discussions/1
    black11black
        105
    black11black  
       2021-01-13 09:44:00 +08:00
    @abersheeran 这么用的话使用上就很舒服了,但是这些 filter map 之类的是自建类吧,感觉不是那么的完美,像 LZ 这个实现,每个函数外面还要加个 F (),感觉也很不 pythonic 。我大概把我上面的想法写了一下是这样

    https://gist.github.com/GoodManWEN/351fe48e24d61b8a62adef3e4460259e

    > i = PIPE | range(10) | (map , lambda x:x + 1) | list | set | END
    > {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    > func = lambda x: PIPE | range(x) | (map , lambda x:x + 1) | list | set | END
    > func(5)
    > {1, 2, 3, 4, 5, 6}
    SmiteChow
        106
    SmiteChow  
       2021-01-13 11:33:03 +08:00
    管道应该面向生成器,如果不是则逻辑的实质为不同函数的流式调用,流式调用必须要有 schema 声明以及对应的解释 shema 逻辑才对的。
    SmiteChow
        107
    SmiteChow  
       2021-01-13 11:34:28 +08:00
    所以你把管道和流式调用弄混了
    SmiteChow
        108
    SmiteChow  
       2021-01-13 11:45:35 +08:00
    但这也能说明你的 python 功力不错,赞。
    abersheeran
        109
    abersheeran  
    OP
       2021-01-13 11:56:43 +08:00 via Android
    @SmiteChow 我对管道的理解来自于 Shell,这里指的也是 Shell 那种管道。
    aguesuka
        110
    aguesuka  
       2021-01-13 12:41:00 +08:00 via Android
    管道是一个优先级较低的左结合中缀运算符。(表达式 运算符 函数) 相当于 (函数 表达式)。作为中缀表达式,它的两个类型不同,不满足交换和结合律,不能保证参数 imutable 。我觉得不适合出现在常规的编程语言中。
    而且实现是右结合的,如果左边实现了 lor 应该会有问题。
    lyzh
        111
    lyzh  
       2021-01-13 17:35:07 +08:00
    @SmiteChow 这里没问题,Filter 和 map 这类本身就可以被柯里化绑定成一个单参函数,然后由管道加入调用流
    lyzh
        112
    lyzh  
       2021-01-13 17:36:25 +08:00
    @aguesuka 管道中不宜出现副作用,这和“管道”的定义本身就是相悖的
    SmiteChow
        113
    SmiteChow  
       2021-01-14 10:32:48 +08:00
    @abersheeran Shell 的管道是可以多次读写的(对应过来就是生成器),你可以细心观察一下

    ```
    echo 1 ; sleep 1 ; echo 2|cat
    ```
    SmiteChow
        114
    SmiteChow  
       2021-01-14 10:37:49 +08:00
    @lyzh 对的,你举例是将生成器的职责落到了“管道”两端的子操作上去,但这更加说明“管道”不是真正意义上的管道,仅仅是流式调用。
    yueyoum
        115
    yueyoum  
       2021-01-14 18:30:21 +08:00
    @no1xsyzy 我并没有说 好语言 是要全部占完, 而是 python 一样都沾不上
    no1xsyzy
        116
    no1xsyzy  
       2021-01-14 20:12:03 +08:00
    @yueyoum
    1. 你看来中文运用不是特别好。
    “什么是好语言:”然后罗列特点 123456,然后说好语言不需要全部占完…… 挺微妙的。

    2. 你看来中文理解不是特别好。
    什么叫“两两冲突”?不是说不能全部占完,你能占两样就是登天难。

    3. 我只好建议你抛弃中文了,一方面根据 1. 2. 中文对你似乎有点困难。
    二来,让我稍微改下你的原话 ——
    什么是好语言:
    123456
    中文 一样都没占上
    连最基本的 语言特性, 我敢说 这个帖子里 没有一个人 完全掌握了 中文 的特性
    所以, 自己玩玩可以

    想了想,我建议你使用拉丁语
    yueyoum
        117
    yueyoum  
       2021-01-19 12:12:54 +08:00
    @no1xsyzy

    你自己 去小学重修语文吧

    或者看看 一句名言: better than nothing

    已经 block 了, 不用劳烦你打字回复了
    no1xsyzy
        118
    no1xsyzy  
       2021-01-19 13:16:58 +08:00
    @yueyoum 打滚打得好,当个猪妈宝
    css3
        119
    css3  
       2021-01-26 20:36:48 +08:00
    简介,好用,但没懂是怎么解析出来 | 这个符号的
    css3
        120
    css3  
       2021-01-26 20:39:12 +08:00
    就是或运算符吗
    abersheeran
        121
    abersheeran  
    OP
       2021-01-26 23:20:35 +08:00
    @css3 是的。还有更多用法讨论在 GitHub discussion 里。可以看看。
    vance123
        122
    vance123  
       2021-02-19 02:25:18 +08:00   ❤️ 1
    @abersheeran 虽然已经是很久以前的帖子了, 不过还是挖一下坟吧.

    这样的用法我过去在 Mathematica 里见过, 有语法层面的支持, 被称作 postfix operator, 即用 x ~ f 来表示 f(x).
    在看到楼主帖子后我又搜索了一下, 发现更确切的叫法是 forward pipe operator, 在函数式语言中比较常见, 例如 F#中的`|>`.
    有人试图在 C#里引入该特性( https://github.com/dotnet/roslyn/issues/5445), 虽然没通过, 但双方对利弊展开了精彩的讨论.
    希望能对楼主和其他人有所帮助
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2248 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 00:43 · PVG 08:43 · LAX 17:43 · JFK 20:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.