一文弄懂Python中的Pipeline

1. 引言

如果你是Python中的开发人员,你可能听说过管道Pipeline一词。但管道究竟是什么?它到底有什么用?在这篇博客中,我们将深入探讨Python中管道的概念,以及它们是如何工作的,如何合理利用它来帮助大家编写更干净、更高效的代码。

2. 管道的概念

管道是一种组织并处理一些数据相关的一系列操作或函数的方式。一个运算的输出变为下一个的输入,以此类推,直到获得最终的结果。管道可以被可视化为管道链,数据从一个管道流向另一个管道,在此过程中进行相应的转换或操作。

3. 举个栗子

这里假设我们有一个数字列表,需要我们对该列表依次执行以下操作:

  • 过滤掉奇数
  • 将每个数字乘以10
  • 每个数字加5
  • 计算所得数字的平均值

    在容易想到的方法是编写一个循环,在列表上迭代并逐个应用到上述每一个操作,同时将中间结果存储在一个新列表中。例如:

    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    filtered = []
    for n in numbers:
        if n % 2 == 0:
            filtered.append(n)
    multiplied = []
    for n in filtered:
        multiplied.append(n * 10)
    added = []
    for n in multiplied:
        added.append(n + 5)
    total = 0
    count = 0
    for n in added:
        total += n
        count += 1
    average = total / count
    print(average)
    

    4. 代码优化

    上述代码虽然可以正常运行,但看起来不是很优雅或高效。它创建了三个新的列表,这些列表占用内存并降低了代码的可读性。此外,它还需要编写四个循环,这可能是相对乏味并且容易出错的。

    这里推荐一种更好的方法是使用管道。管道允许大家将一些列操作链接在一起,而无需创建中间列表或循环。大家可以使用内置的map和filter函数,这些函数以一个函数和一个可迭代对象作为参数,并返回一个新的可迭代对象,将该函数应用于原始可迭代对象中的每个元素。当然你也可以使用sum和len函数,可分别计算可迭代项的总和和长度。例如:

    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    average = sum(map(lambda n: n + 5, map(lambda n: n * 10,  \
    	filter(lambda n: n % 2 == 0, numbers)))) / len(numbers)
    print(average)
    

    这段代码比上一段要简短得多,也更加简单。它不创建任何新的列表或循环,而且很容易看到从一个操作到下一个操作的数据流。然而,它仍然不太可读,因为它使用了嵌套的map和filter调用,这可能会令人感到困惑并且难以理解。

    5. 使用管道

    编写管道Pipeline的一种更加具有可读性的方法是使用toolz库中的管道函数。管道函数采用一个初始值和一系列函数,并将每个函数应用于前一个函数的输出,返回最终结果。例如:

    from toolz import pipe
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    average = pipe(numbers, 
                   filter(lambda n: n % 2 == 0), 
                   map(lambda n: n * 10), 
                   map(lambda n: n + 5), 
                   lambda x: sum(x) / len(x))
    print(average)
    

    这段代码比之前的代码可读性更强,也更优雅。它清楚地显示了应用于数据的操作序列,并且不使用任何嵌套调用或中间列表。管道函数使代码看起来像一个管道,使得数据从一个函数流到下一个函数。

    6. 为什么使用管道?

    与其他组织代码的方式相比,管道具有以下几个优势。其中一些是:

    • 可读性:管道使代码更具可读性和可理解性,因为它们显示了从一个操作到下一个操作的数据流,而不会使代码与中间变量或循环混淆。管道还使代码更加模块化和可重用,因为每个操作都可以定义为一个单独的函数,可以在不同的上下文中轻松测试和复用。

    • 效率:管道可以提高代码的效率,因为它们可以避免创建占用内存和减慢执行速度的中间列表或迭代。管道还允许延迟评估,这意味着操作只在需要数据时执行,而不是在需要数据之前执行。这可以节省时间和资源,尤其是在处理大型或无限数据源时。

    • 灵活性:管道可以很容易地修改或扩展,因为它们允许添加、删除或更改操作的顺序,而不会影响代码的其余部分。管道还支持不同类型的操作,如 filtering, mapping, reducing, aggregating, grouping, sorting,等,这些操作可以以各种方式组合起来,以获得不同的结果。

      7. 如何使用管道?

      在Python中有不同的方法来创建和使用管道。举例如下:

      • 使用内置函数:Python提供了几个可用于创建管道的内置函数,如map、filter、reduce、zip、enumerate、sorted、reverse等。这些函数以一个函数和一个可迭代对象为参数,并返回一个新的可迭代对象,该可迭代对象将该函数应用于原始可迭代对象的每个元素。
      • 使用列表生成式:列表生成式是在Python中使用类似数学符号的语法创建列表的一种简洁而富有表现力的方式。列表生成式也可以用于创建管道,方法是对可迭代的每个元素应用一系列操作,并将结果收集到一个新列表中。
      • 使用生成器表达式:生成器表达式类似于列表生成式,但它们返回生成器对象而不是列表。生成器对象是一个可迭代对象,它根据需要生成元素,而不将它们存储在内存中。生成器表达式还可以用于创建管道,方法是将一系列操作应用于可迭代的每个元素,并将结果作为生成器生成。

        8. 总结

        本文重点介绍了管道的定义,以及相应的优点和具体的使用方法,并给出了相应的代码示例。

        您学废了嘛?