链接地图和过滤器的 Python 方式是什么?

新手上路,请多包涵

我目前正在学习 Python(来自 JavaScript 和 Ruby 等其他语言)。我非常习惯链接一堆转换/过滤器,但我很确定这不是在 Python 中执行此操作的正确方法: filter 在可枚举之前采用 lambda,因此编写 long / multi -line 函数看起来真的很奇怪,将它们链接在一起意味着将它们按相反的顺序放置,这是不可读的。

在此 JavaScript 函数中编写映射和过滤器的“Python 方式”是什么?

 let is_in_stock = function() /* ... */
let as_item = function() /* ... */

let low_weight_items = shop.inventory
    .map(as_item)
    .filter(is_in_stock)
    .filter(item => item.weight < 1000)
    .map(item => {
        if (item.type == "cake") {
            let catalog_item = retrieve_catalog_item(item.id);

            return {
                id: item.id,
                weight: item.weight,
                barcode: catalog_item.barcode
            };
        } else {
            return default_transformer(item);
        }
    });

我知道我可能会对第一张地图和接下来的两个过滤器使用列表理解,但我不确定如何做最后一张地图以及如何将所有内容放在一起。

谢谢!

原文由 Edward 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 767
2 个回答

一个好的方法是将多个过滤器/映射组合成一个生成器理解。在无法做到这一点的情况下,为您需要的中间映射/过滤器定义一个中间变量,而不是试图将映射强制到一个链中。例如:

 def is_in_stock(x):
   # ...
def as_item(x):
   # ...
def transform(item):
    if item.type == "cake":
        catalog_item = retrieve_catalog_item(item.id)
        return {
            "id": item.id,
            "weight": item.weight,
            "barcode": catalog_item.barcode
        }
    else:
        return default_transformer(item)

items = (as_item(item) for item in shop.inventory)
low_weight_items = (transform(item) for item in items if is_in_stock(item) and item.weight < 1000)

请注意,地图和过滤器的实际应用都在最后两行中完成。前面的部分只是定义对映射和过滤器进行编码的函数。

第二个生成器理解将最后两个过滤器和映射一起执行。使用生成器理解意味着 inventory 中的每个原始项目将被延迟映射/过滤。它不会预处理整个列表,因此如果列表很大,它的性能可能会更好。

请注意,没有 Python 等同于在您的 JavaScript 示例中定义内联长函数。您不能内联指定复杂过滤器(带有 item.type == "cake" 的过滤器)。相反,如我的示例所示,您 必须 将其定义为单独的函数,就像您对 is_in_stockas_item 的那样。

(第一个地图被拆分的原因是后面的过滤器在映射之后才能对映射数据进行操作。它可以组合成一个,但这需要手动重做 as_item 理解内的地图:

 low_weight_items = (transform(as_item(item)) for item in items if is_in_stock(as_item(item)) and as_item(item).weight < 1000)

把那张地图分开会更清楚。)

原文由 BrenBarn 发布,翻译遵循 CC BY-SA 3.0 许可协议

如果您不介意使用包,这是使用 https://github.com/EntilZha/PyFunctional 的另一种方法

from functional import seq

def as_item(x):
    # Implementation here
    return x

def is_in_stock(x):
    # Implementation
    return True

def transform(item):
    if item.type == "cake":
        catalog_item = retrieve_catalog_item(item.id);
        return {
            'id': item.id,
            'weight': item.weight,
            'barcode': catalog_item.barcode
        }
    else:
        return default_transformer(item)

low_weight_items = seq(inventory)\
    .map(as_item)\
    .filter(is_in_stock)\
    .filter(lambda item: item.weight < 1000)\
    .map(transform)

如前所述,python 允许您使用 lamdba 表达式,但它们不如 javascript 中的 clojure 灵活,因为它们不能有多个语句。另一个烦人的 python 事情是需要反斜杠。话虽如此,我认为以上内容最接近您最初发布的内容。

免责声明:我是上述包的作者

原文由 EntilZha 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏