代码之家  ›  专栏  ›  技术社区  ›  norok2

嵌套可数映射/筛选

  •  2
  • norok2  · 技术社区  · 7 年前

    我想在Python中映射和过滤符合特定条件的嵌套iterables元素,比如:

    items = [func(item) if map_condition(item) else item for item in items if filter_condition(item)]
    

    但被推广到嵌套的iterables,例如输入:

    items = [[1, 2], [[3, 4, 4.0, 5], 5], 6.0, 'ciao', 7, 8, {1, 2, 3}]
    

    应用函数 nested_map_filter(items, func, map_condition, filter_condition) 像这样:

    new_items = nested_filter_map(items, str, lambda x: isinstance(x, int), lambda x: not isinstance(x, str))
    new_items = nested_filter_map(items, str, None, None)
    new_items = nested_filter_map(items, str, None, lambda x: not isinstance(x, float))
    new_items = nested_filter_map(items, str, lambda x: isinstance(x, int), None)
    new_items = nested_filter_map(items, str, lambda x: isinstance(x, int), lambda x: not isinstance(x, float))
    

    将分别导致:

    # [['1', '2'], [['3', '4', 4.0, '5'], '5'], 6.0, '7', '8', {'2', '1', '3'}]
    # [['1', '2'], [['3', '4', '4.0', '5'], '5'], '6.0', 'ciao', '7', '8', {'2', '1', '3'}]
    # [['1', '2'], [['3', '4', '5'], '5'], 'ciao', '7', '8', {'2', '1', '3'}]
    # [['1', '2'], [['3', '4', 4.0, '5'], '5'], 6.0, 'ciao', '7', '8', {'2', '1', '3'}]
    # [['1', '2'], [['3', '4', '5'], '5'], 'ciao', '7', '8', {'2', '1', '3'}]
    

    标准库中是否有标准构造或其他东西来实现这一点?

    编辑 :我改进了术语,以便 map / filter


    编辑

    我写过这样的东西,很管用。

    ( 编辑2 )我的问题更多:我是在重新发明轮子吗?是否可以/方便地将其作为生成器编写?

    def deep_filter_map(
            items,
            func=None,
            map_condition=None,
            filter_condition=None,
            avoid=(str, bytes),
            max_depth=-1):
        if func is None:
            def func(x): return x
        if map_condition is None:
            def map_condition(_): return True
        if filter_condition is None:
            def filter_condition(_): return True
        container = type(items)
        new_items = []
        for item in items:
            try:
                no_expand = avoid and isinstance(item, avoid)
                if no_expand or max_depth == 0 or item == next(iter(item)):
                    raise TypeError
            except TypeError:
                if filter_condition(item):
                    new_items.append(func(item) if map_condition(item) else item)
            else:
                new_items.append(
                    deep_filter_map(
                        item, func, map_condition, filter_condition, avoid, max_depth - 1))
        return container(new_items)
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   tobias_k    7 年前

    据我所知,标准库中没有这样的功能,但您可以通过将解决方案拆分为更小的部分,然后独立使用,从而提高其可重用性(以及可维护性和可测试性)。首先是一个包装器函数,用于有条件地应用某些函数,然后是两个函数,用于将任何函数应用于或有条件地过滤嵌套iterables的元素。

    def cond_apply(cond, func):
        return lambda x: func(x) if cond(x) else x
    
    def deep_map(func, lst, types=(list, tuple, set)):
        if isinstance(lst, types):
            return type(lst)(deep_map(func, x) for x in lst)
        else:
            return func(lst)
    
    def deep_filter(cond, lst, types=(list, tuple, set)):
        if isinstance(lst, types):
            return type(lst)(deep_filter(cond, x) for x in lst if isinstance(x, types) or cond(x))
        else:
            return lst
    
    items = [[1, 2], [[3, 4, 4.0, 5], 5], 6.0, 'ciao', 7, 8, {1, 2, 3}]
    print(deep_map(cond_apply(lambda x: isinstance(x, int), str),
                   deep_filter(lambda x: not isinstance(x, str), items)))
    # [['1', '2'], [['3', '4', 4.0, '5'], '5'], 6.0, '7', '8', {'1', '3', '2'}]
    

    通过这种方式,该功能可以单独使用,也可以反转 map filter 台阶。当然,您仍然可以将这三个函数包装成另一个函数,提供所有参数,以便于使用。

    推荐文章