AberSheeran
Aber Sheeran

四行代码实现 Python 管道

起笔自
所属文集: 杂记
共计 1519 个字符
落笔于

在我编写从 HBase 读取并分析数据的代码时候,遇到了一个问题——多重循环嵌套。

众所周知,HBase 本身是没有二级索引的,所有的数据分析都只能靠扫描 rowkey 读出一整个列表的数据,然后再自己提炼加工。而提炼加工的过程就是不断地循环数据列表,对每个数据项进行筛选、修改的过程。

这让我写的很痛苦,我不得不反复不断地嵌套循环。于是我想起了管道。

from functools import partial

class F(partial):
    def __ror__(self, other):
        return self(other)

这就是全部的 Python 代码。无论是 Python2 还是 Python3 这段代码都有效。

看看管道的魅力!

以下代码均基于 Python3,你可以自行在 Python2 中编写代码进行实践。

求十以下所有奇数的合:

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

筛选字典中值为真的数据:

your_dict | F(lambda d: {k: v for k, v in d.items() if v})

接下来是一点实际应用,以下是一段满足需求的代码:

result = {}

for method in view.__methods__:
    if method == "OPTIONS":
        continue
    method = method.lower()
    method_docs = self._generate_method(
        getattr(view, method), path, definitions
    )
    if not method_docs:
        continue
    result[method] = method_docs

由于这段 for 循环里的代码过多,无法使用推导式进行优化结构。看看完成相同的功能但使用管道的代码:

generate_method_docs = lambda method: (
    method,
    self._generate_method(getattr(view, method), path, definitions),
)
result = dict(
    view.__methods__
    | F(map, lambda method: method.lower())
    | F(filter, lambda method: method != "options")
    | F(map, generate_method_docs)
    | F(filter, lambda method_and_docs: bool(method_and_docs[1]))
)

管道中每个函数完成自己的功能,并且不会出现类似于 dict(filter(map(filter(map(...), ...), ...), ...)) 这种嵌套的不友好情况。管道操作使得数据的流向和阅读思考代码的顺序达成一致,虽然在性能上有微不足道的损耗。但,你都开始使用 Python 了,在自己的业务代码上扣那么几十μs的性能(此数字经过 CPython3.7、3.8 的测试得到)似乎没有必要?可能你把 Python 版本从 3.6 升级到 3.9 就足够弥补这方面的损耗了(笑)。

如果你觉得本文值得,不妨赏杯茶
如何索引十亿哈希数据
多线程真的能加速 os.stat 吗?