【问题标题】:Python list comprehension to create unequal length lists from a list using conditionalPython列表推导使用条件从列表中创建不等长度的列表
【发布时间】:2012-04-24 21:03:33
【问题描述】:

使用列表解析、itertools 或类似函数,是否可以根据条件从一个列表创建两个不相等的列表?这是一个例子:

main_list = [6, 3, 4, 0, 9, 1]
part_list = [4, 5, 1, 2, 7]

in_main = []
out_main = []

for p in part_list:
    if p not in main_list:
        out_main.append(p)
    else:
        in_main.append(p)

>>> out_main
[5, 2, 7]

>>> in_main
[4, 1]

我试图保持简单,但作为一个使用示例,main_list 可以是来自字典的值,其中part_list 包含字典键。我需要同时生成两个列表。

【问题讨论】:

  • 我觉得这可以用集合来完成
  • @JakobBowyer main_list 值可以从字典中生成。 sets 是显而易见的解决方案,但要寻找同时生成两个列表的单个列表理解或 itertools 类型的解决方案。

标签: python generator list-comprehension


【解决方案1】:

如果顺序(part_list 内)很重要:

out_main = [p for p in part_list if p not in main_list]
in_main = [p for p in part_list if p in main_list]

否则:

out_main = list(set(part_list) - set(main_list))
in_main = list(set(part_list) & set(main_list))

【讨论】:

  • 我发誓我先写了集合答案:P
【解决方案2】:

只要你没有重复的数据&顺序无关紧要。

main_set = set([6, 3, 4, 0, 9, 1])
part_set = set([4, 5, 1, 2, 7])

out_main = part_set - main_set
in_main = part_set & main_set

工作完成。

【讨论】:

  • 只要顺序无关紧要。
  • 变量想命名为main_setpart_set :(
【解决方案3】:
in_main = list(set(main_list) & set(part_list))
out_main = list(set(part_list) - set(in_main))

【讨论】:

    【解决方案4】:

    一个真正的基于itertools 的解决方案,适用于可迭代对象:

    >>> part_iter = iter(part_list)
    >>> part_in, part_out = itertools.tee(part_iter)
    >>> in_main = (p for p in part_in if p in main_list)
    >>> out_main = (p for p in part_out if p not in main_list)
    

    从这些列表中创建列表会破坏使用迭代器的意义,但结果如下:

    >>> list(in_main)
    [4, 1]
    >>> list(out_main)
    [5, 2, 7]
    

    这具有从另一个延迟生成的序列延迟生成in_mainout_main 的优点。唯一的问题是,如果您在另一个之前迭代一个,tee 必须缓存一堆数据,直到它被另一个迭代器使用。因此,仅当您大致同时遍历它们时,这才真正有用。否则你还不如自己使用辅助存储。

    还有一个有趣的基于三元运算符的解决方案。 (你可以把它压缩成一个列表理解,但那是错误的。)我将 main_list 更改为一个用于 O(1) 查找的集合。

    >>> main_set = set(main_list)
    >>> in_main = []
    >>> out_main = []
    >>> for p in part_list:
    ...     (in_main if p in main_set else out_main).append(p)
    ... 
    >>> in_main
    [4, 1]
    >>> out_main
    [5, 2, 7]
    

    还有一个有趣的collections.defaultdict 方法:

    >>> import collections
    >>> in_out = collections.defaultdict(list)
    >>> for p in part_list:
    ...     in_out[p in main_list].append(p)
    ... 
    >>> in_out
    defaultdict(<type 'list'>, {False: [5, 2, 7], True: [4, 1]})
    

    【讨论】:

    • +1 表示第二种解决方案,但不确定 itertools 解决方案。
    • 似乎做得太过分了,它还涉及压缩最终结果并将Nonein_mainout_main中过滤出来。
    • @jamylak,啊,那只是为了显示生成器的内容。我会改写...
    • 我喜欢defaultdict,似乎比有两个变量更好:D
    • 在defaultdict解决方案中,它如何自动创建'False'列表?
    【解决方案5】:

    从谓词列表开始:

    test_func = [part_list.__contains__, lambda x: not part_list.__contains__(x)]
    # Basically, each of the predicates is a function that returns a True/False value     
    # (or similar) according to a certain condition.
    # Here, you wanted to test set intersection; but you could have more predicates.
    
    print [filter(func, main_list) for func in test_func]
    

    然后你有你的“单线”,但你有一些维护谓词列表的开销工作

    正如在其他答案中所说,您可以改用 set(main_list) 来加快查找速度(当然不是在列表理解中,而是在之前)。

    【讨论】:

    • 我对downvotes 非常满意,但您愿意解释一下原因吗?谢谢!
    • 我给你投了赞成票,因为我讨厌人们在没有反馈的情况下投反对票。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 2017-02-17
    相关资源
    最近更新 更多