【问题标题】:Using break in a list comprehension在列表理解中使用 break
【发布时间】:2019-05-27 11:14:10
【问题描述】:

如何根据条件(例如找到数字 412 时)打破列表理解?

代码:

numbers = [951, 402, 984, 651, 360, 69, 408, 319, 601, 485, 980, 507, 725, 547, 544,
           615, 83, 165, 141, 501, 263, 617, 865, 575, 219, 390, 984, 592, 236, 105, 942, 941,
           386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399,
           162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67,
           104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958, 609, 842,
           451, 688, 753, 854, 685, 93, 857, 440, 380, 126, 721, 328, 753, 470, 743, 527]

even = [n for n in numbers if 0 == n % 2]

所以在功能上,你可以推断这是应该做的事情:

even = [n for n in numbers if 0 == n % 2 and break if n == 412]

真的更喜欢:

  • 单线
  • 没有其他花哨的库,如 itertools,如果可能,“纯 python”(阅读:解决方案不应使用任何 import 语句或类似语句)

【问题讨论】:

  • itertools 是纯python。
  • ...itertools 是 Python ...总的来说,这听起来像是一个普通的 for 循环的工作。
  • @Flavius:为什么从 Python 自己的库中导入一些东西不是“炫耀它的力量”?
  • @Flavius:所以你试图让你的同事相信你可以用 Python 写一些粗俗和丑陋的东西,这会以某种方式给他们留下深刻印象?
  • @Flavius,Python 的强大之处在于其标准库。如果更多的标准库函数是内置的,Python 会“更强大”吗?我想不是。无论哪种方式,它们都是标准的,但如果它们是内置的,那么命名空间冲突的可能性就会更大。最好将它们隔离开来。

标签: python list-comprehension


【解决方案1】:

考虑到生成器解决方案已经过时,我想出了以下内容:

even = [n for n in next((numbers[:i] for i, n in enumerate(numbers) if n == 412)) if not n % 2]

然后我回去看到了 Andrew Clark 的答案,答案是一样的,但要好得多

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

无论切片解决方案的最佳部分是,您都可以选择在结束元素的任一侧包含或排除一些元素,例如获得 412 和后面的数字:

even = [n for n in numbers[:numbers.index(412)+2] if not n % 2]

【讨论】:

    【解决方案2】:

    end 条件的帮助下,解决breaking in list comprehension 的另一个偷偷摸摸的单行解决方案。

    不使用numbers.index(412),可能会快一点?

    even = [n for end in [[]] for n in numbers
            if (False if end or n != 412 else end.append(42))
            or not end and not n % 2]
    

    注意:这是个坏主意。 只是为了好玩:)

    正如@WolframH 所说:

    对于那些抱怨它是 hackish 并且不应该在生产代码中使用的人:嗯,你是对的。 一定。

    【讨论】:

      【解决方案3】:

      我知道这是一个非常古老的帖子,但是由于 OP 询问在 list-comprehension 中使用 break 并且我也在寻找类似的东西,我想我会在这里发布我的发现以供将来参考。

      在调查break 时,我遇到了iter 鲜为人知的功能iter(callable, sentinel),它返回一个迭代器,一旦可调用function 值等于@987654327 @值。

      >>> help(iter)
      Help on built-in function iter in module __builtin__:
      
      iter(...)
          iter(collection) -> iterator
          iter(callable, sentinel) -> iterator
      
          Get an iterator from an object.  In the first form, the argument must
          supply its own iterator, or be a sequence.
          In the second form, the callable is called until it returns the sentinel.
      

      这里的棘手部分是定义一个适合给定问题的函数。在这种情况下,首先我们需要将给定的numbers 中的list 转换为iterator,使用x = iter(numbers) 作为外部变量输入lambda 函数。

      接下来,我们的可调用函数只是调用迭代器以吐出下一个值。然后迭代器与我们的标记值(在本例中为 412)进行比较,并在达到该值时“中断”。

      print [i for i in iter(lambda x=iter(numbers): next(x),412) if i %2 == 0]
      
      >>> 
      [402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418,  
       344, 236, 566, 978, 328, 162, 758, 918]
      

      【讨论】:

      • 你能让它在之后达到价值,而不是之前停止吗?
      • 我不这么认为。一旦可调用函数值等于标记值,根据定义,这种安排就会“中断”迭代。您可能想查看@WolframH 解决方案并修改功能以满足您的需求。
      【解决方案4】:
      even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2] 
      

      刚刚获取了上面 F.J. 的代码并添加了一个三元来检查 412 是否在列表中。仍然是“单线”,即使 412 不在列表中也可以使用。

      【讨论】:

      • 如果 412 不在 numbers 中,如果是偶数,则最后一个元素丢失。
      • F.J. 是谁,“上面的代码”在哪里?不要将 Stack Overflow 误认为是(传统的)论坛;根据tour,答案可能会上升到顶部或下降到底部。
      • @usr2564301。我会记住这一点。当我在 6 年前回答这个问题时,肯定有另一个评分高于此的答案我改进了。
      • 你还记得你修改的是哪个答案吗? :)
      • 感谢所有为这篇文章提供建议的人。然而,这种做法只会让不太熟练的人在 12 个月后试图理解这一点感到沮丧。我们能否避免“单行”导致“一小时”尝试解码它们?
      【解决方案5】:

      使用函数引发 StopIterationlist 来捕获它:

      >>> def end_of_loop():
      ...     raise StopIteration
      ... 
      >>> even = list(end_of_loop() if n == 412 else n for n in numbers if 0 == n % 2)
      >>> print(even)
      [402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418, 344, 236, 566, 978, 328, 162, 758, 918]
      

      对于那些抱怨它不是单行的:

      even = list(next(iter(())) if n == 412 else n for n in numbers if 0 == n % 2)
      

      对于那些抱怨它是 hackish 并且不应该在生产代码中使用的人:嗯,你是对的。 一定。

      【讨论】:

      • 根据我的理解,这不是列表理解,但它是单行的(我会这样认为,因为仅使用该函数是因为 @987654326 在 python 中存在无法解释的限制@ 声明),它似乎没有任何缺点。 +1ed
      • +0。聪明的。但是很老套,而不是单线。
      • 在 Python 3+ 中是否有符合犹太教规的修复?
      • @Zuza PEP 的"Examples of breakage" 部分包含一个非常相似的示例。你可以在最后找到它。单行解决方法将不再可能 - 您需要定义一个生成器函数。
      • RuntimeError: generator raised StopIteration
      【解决方案6】:

      列表显示的语法(包括列表推导)在这里:http://docs.python.org/reference/expressions.html#list-displays

      如您所见,没有特殊的whileuntil 语法。你能得到的最接近的是:

      even_numbers = (n for n in numbers if 0 == n % 2)
      list(itertools.takewhile(lambda x: x != 412, even_numbers))
      

      (代码取自 Sven Marnach 的回答,在我打字时发布)。

      【讨论】:

      • 我没有投反对票,但我认为这是因为您从其他人那里获取了代码。至少你承认了。我会赞成的。
      【解决方案7】:

      您可以将生成器表达式与itertools.takewhile() 一起使用:

      even_numbers = (n for n in numbers if not n % 2)
      list(itertools.takewhile(lambda x: x != 412, even_numbers))
      

      编辑:我刚刚注意到不使用任何imports 的要求。好吧,我还是把这个答案留在这里。

      【讨论】:

      • 如果他真的想要他的一个班轮:[n for n in itertools.takewhile(lambda x: x != 412, numbers) if not n % 2]
      【解决方案8】:

      如果 412 肯定会在列表中,你可以使用这个:

      even = [n for n in numbers[:numbers.index(412)] if not n % 2]
      

      如果您想在结果中包含 412,只需使用 numbers[:numbers.index(412)+1] 作为切片。

      请注意,由于切片,这将比 itertools 或 for 循环解决方案效率低(至少在内存方面)。

      【讨论】:

      • 不仅因为切片效率较低,还必须对列表进行线性搜索。
      • @FelixKling - 然而,在与其他答案的快速时间测试中,这对于提供的样本数据来说更快。不过,随着数据集的增加,我肯定希望其他人通过它。
      • @FelixKling:对 412 的线性搜索是超快的 C 代码,而其他解决方案在 Python 代码中测试 412,一些解决方案为 调用函数(这在 CPython 中很昂贵)每个数字。我相信这个解决方案更快! -- 列表复制也是用非常快的 C 代码完成的;除非你的内存很紧,否则不应该有性能问题。 (好吧,如果第一个数字是 412 并且列表有 10**6 个条目,那就不好了;但是如果 412 是最后一个数字,这个解决方案应该仍然非常非常快。)
      • 考虑到之前的评论,+1ed。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多