【问题标题】:Pythonic way to combine FOR loop and IF statement结合 FOR 循环和 IF 语句的 Pythonic 方式
【发布时间】:2026-01-29 02:55:02
【问题描述】:

我知道如何在单独的行上同时使用 for 循环和 if 语句,例如:

>>> a = [2,3,4,5,6,7,8,9,0]
... xyz = [0,12,4,6,242,7,9]
... for x in xyz:
...     if x in a:
...         print(x)
0,4,6,7,9

而且我知道当语句很简单时,我可以使用列表推导来组合这些,例如:

print([x for x in xyz if x in a])

但是我在任何地方都找不到一个很好的例子(可以复制和学习),它展示了在 for 循环和一些 if 语句的组合之后发生的一组复杂的命令(不仅仅是“打印 x”)。我期望的东西看起来像:

for x in xyz if x not in a:
    print(x...)

这不是 python 应该工作的方式吗?

【问题讨论】:

  • 就是这样......不要试图简化它们而使事情变得过于复杂。 Pythonic 并不意味着要避免每个显式的for 循环和if 语句。
  • 您可以在 for 循环中使用列表推导中生成的列表。这有点像你的最后一个例子。
  • @Chewy,正确的数据结构将使代码更快,而不是语法糖。例如,如果a 是一个列表,x in a 就会很慢。
  • 这是 Python,一种解释型语言;为什么有人在讨论代码有多快?
  • @ArtOfWarfare 可能是因为它被用在了不应该使用的地方。速度真的很重要。

标签: python loops if-statement for-loop


【解决方案1】:

你可以像这样使用generator expressions

gen = (x for x in xyz if x not in a)

for x in gen:
    print(x)

【讨论】:

  • gen = (y for (x,y) in enumerate(xyz) if x not in a) 返回 >>> 12 当我输入 for x in gen: print x 时——那么为什么使用 enumerate 会出现意外行为?
  • 可能,但不如原来的 for 和 if 块。
  • @ChewyChunks。这可行,但枚举调用是多余的。
  • 我真的很怀念python能够说for x in xyz if x:
  • for x in (x for x in xyz if x not in a): 为我工作,但为什么你不应该只是能够做到for x in xyz if x not in a:,我不确定......
【解决方案2】:
a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]  
set(a) & set(xyz)  
set([0, 9, 4, 6, 7])

【讨论】:

  • 非常禅,@lazyr,但不会帮助我改进依赖于遍历一个列表并忽略另一个列表中的匹配元素的复杂代码块。将第一个列表视为一个集合并将联合/差异与第二个不断增长的“忽略”列表进行比较是否更快?
  • 试试这个import time a = [2,3,4,5,6,7,8,9,0] xyz = [0,12,4,6,242,7,9] start = time.time() print (set(a) & set(xyz)) print time.time() - start
  • @ChewyChunks 如果任何一个列表在迭代期间发生更改,则根据忽略列表检查每个元素可能会更快——除非您应该将其设为忽略集。检查集合中的成员非常快:if x in ignore: ....
  • @lazyr 我只是在忽略列表上使用 ignore set 重写了我的代码。似乎处理时间要慢得多。 (公平地说,我使用if set(a) - set(ignore) == set([]): 进行比较,所以也许这就是它比检查成员资格慢得多的原因。我以后会在一个比我写的更简单的例子上再次测试这个。
【解决方案3】:

根据The Zen of Python(如果您想知道您的代码是否是“Pythonic”,那就去那里):

  • 美胜于丑。
  • 显式优于隐式。
  • 简单胜于复杂。
  • 平面优于嵌套。
  • 可读性很重要。

获取两个sets 中的sorted intersection 的Pythonic 方式是:

>>> sorted(set(a).intersection(xyz))
[0, 4, 6, 7, 9]

或者那些在xyz但不在a中的元素:

>>> sorted(set(xyz).difference(a))
[12, 242]

但对于更复杂的循环,您可能希望通过迭代一个命名良好的 generator expression 和/或调用一个命名良好的函数来将其展平。试图将所有内容都放在一行中很少是“Pythonic”。


更新以下关于您的问题和接受的答案的附加 cmets

我不确定你想用 enumerate 做什么,但如果 a 是一个字典,你可能想要使用这些键,如下所示:

>>> a = {
...     2: 'Turtle Doves',
...     3: 'French Hens',
...     4: 'Colly Birds',
...     5: 'Gold Rings',
...     6: 'Geese-a-Laying',
...     7: 'Swans-a-Swimming',
...     8: 'Maids-a-Milking',
...     9: 'Ladies Dancing',
...     0: 'Camel Books',
... }
>>>
>>> xyz = [0, 12, 4, 6, 242, 7, 9]
>>>
>>> known_things = sorted(set(a.iterkeys()).intersection(xyz))
>>> unknown_things = sorted(set(xyz).difference(a.iterkeys()))
>>>
>>> for thing in known_things:
...     print 'I know about', a[thing]
...
I know about Camel Books
I know about Colly Birds
I know about Geese-a-Laying
I know about Swans-a-Swimming
I know about Ladies Dancing
>>> print '...but...'
...but...
>>>
>>> for thing in unknown_things:
...     print "I don't know what happened on the {0}th day of Christmas".format(thing)
...
I don't know what happened on the 12th day of Christmas
I don't know what happened on the 242th day of Christmas

【讨论】:

  • 听起来像下面的 cmets,我应该研究发电机。我从来没有用过它们。谢谢。生成器是否比 FOR 和 IF 语句的等效组合更快?我也使用过集合,但有时列表中的冗余元素是我无法丢弃的信息。
  • @ChewyChunks:生成器并不是 Pythonic 的唯一途径!
  • @Johnsyweb,如果你要引用 Python 的禅宗:“应该有一种——最好只有一种——明显的方法。”
  • @Wooble:应该有。我大约在同一时间在my answer to another question 中引用了该部分!
  • python 语言在 python 禅宗的三个方面都失败了:我不同意其他三个(显式、简单、扁平)。我不是新手:30 个月以来它一直是我的主要语言,自 2012 年以来,我每年都在使用它进行重大项目。这个评论离题了吗?鉴于禅宗在这个问题上得到了缓解,不一定
【解决方案4】:

如果生成器表达式过于复杂或过于复杂,您也可以使用generators

def gen():
    for x in xyz:
        if x in a:
            yield x

for x in gen():
    print x

【讨论】:

  • 这对我来说有点用处。我从来没有看过发电机。它们听起来很吓人(因为我在通常很难使用的模块中看到它们)。
【解决方案5】:

我个人认为这是最漂亮的版本:

a = [2,3,4,5,6,7,8,9,0]
xyz = [0,12,4,6,242,7,9]
for x in filter(lambda w: w in a, xyz):
  print x

编辑

如果您非常热衷于避免使用 lambda,您可以使用部分函数应用程序并使用 operator 模块(提供大多数运算符的功能)。

https://docs.python.org/2/library/operator.html#module-operator

from operator import contains
from functools import partial
print(list(filter(partial(contains, a), xyz)))

【讨论】:

  • filter(a.__contains__, xyz)。通常当人们使用 lambda 时,他们确实需要更简单的东西。
  • 我认为你误解了一些东西。 __contains__ 是一个和其他方法一样的方法,只是它是一个 special 方法,这意味着它可以被操作员间接调用(在这种情况下为in)。但它也可以直接调用,它是公共 API 的一部分。私有名称被明确定义为最多有一个尾随下划线,以提供特殊方法名称的例外 - 并且它们在类范围内的词法上受到名称修饰的影响。见docs.python.org/3/reference/datamodel.html#specialnamesdocs.python.org/3.6/tutorial/classes.html#private-variables
  • 当然没问题,但是两个导入只是为了能够引用一个仅使用属性即可访问的方法似乎很奇怪(操作符通常在双重调度必不可少时使用,但 in 是单独的发送了正确的操作数)。此外,请注意operator 还以__contains__ 的名称导出contains 方法,因此它肯定不是私有名称。我认为您只需要学会接受并非每个双下划线都意味着“远离”的事实。 :-]
  • 我认为您的 lambda 需要修复以包含 not : lambda w: not w in a, xyz
  • 过滤器看起来更优雅,特别是对于将成为定义函数而不是 lambdas 的复杂条件,也许命名 lambda 函数会增加一些可读性,当迭代元素对列出项目
【解决方案6】:

我可能会使用:

for x in xyz: 
    if x not in a:
        print(x...)

【讨论】:

  • @KirillTitov 是的,python 从根本上来说是一种非功能性语言(这是一种纯粹的命令式编码 - 我同意这个答案的作者,即这是设置 python 的编写方式。尝试使用函数式导致阅读不佳或非pythonic 结果。我可以用我使用的所有其他语言(scala、kotlin、javascript、R、swift ......)进行函数式编码,但在 python 中困难/尴尬
【解决方案7】:

使用intersectionintersection_update

  • 交叉口

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    ans = sorted(set(a).intersection(set(xyz)))
    
  • intersection_update

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    b = set(a)
    b.intersection_update(xyz)
    

    那么b就是你的答案

【讨论】:

    【解决方案8】:

    以下是已接受答案的简化/一条线:

    a = [2,3,4,5,6,7,8,9,0]
    xyz = [0,12,4,6,242,7,9]
    
    for x in (x for x in xyz if x not in a):
        print(x)
    
    12
    242
    

    注意generator 保持内联。这是在python2.7python3.6 上测试的(注意print 中的括号;))

    尽管如此,这确实很麻烦:x 被提及 四次 次。

    【讨论】:

      【解决方案9】:

      我喜欢 Alex's answer,因为 filter 正是应用于列表的 if,所以如果您想在给定条件的情况下探索列表的子集,这似乎是最自然的方式

      mylist = [1,2,3,4,5]
      another_list = [2,3,4]
      
      wanted = lambda x:x in another_list
      
      for x in filter(wanted, mylist):
          print(x)
      

      这种方法对于分离关注点很有用,如果条件函数发生变化,唯一需要摆弄的代码就是函数本身

      mylist = [1,2,3,4,5]
      
      wanted = lambda x:(x**0.5) > 10**0.3
      
      for x in filter(wanted, mylist):
          print(x)
      

      generator 方法在您不想要列表成员时似乎更好,但对所述成员进行修改,这似乎更适合 generator

      mylist = [1,2,3,4,5]
      
      wanted = lambda x:(x**0.5) > 10**0.3
      
      generator = (x**0.5 for x in mylist if wanted(x))
      
      for x in generator:
          print(x)
      

      此外,过滤器也可以与生成器一起使用,尽管在这种情况下效率不高

      mylist = [1,2,3,4,5]
      
      wanted = lambda x:(x**0.5) > 10**0.3
      
      generator = (x**0.9 for x in mylist)
      
      for x in filter(wanted, generator):
          print(x)
      

      当然,这样写还是不错的:

      mylist = [1,2,3,4,5]
      
      wanted = lambda x:(x**0.5) > 10**0.3
      
      # for x in filter(wanted, mylist):
      for x in mylist if wanted(x):
          print(x)
      

      【讨论】:

        【解决方案10】:

        查找列表 a 和 b 的唯一共同元素的简单方法:

        a = [1,2,3]
        b = [3,6,2]
        for both in set(a) & set(b):
            print(both)
        

        【讨论】:

          【解决方案11】:

          基于此处的文章:https://towardsdatascience.com/a-comprehensive-hands-on-guide-to-transfer-learning-with-real-world-applications-in-deep-learning-212bf3b2f27a 出于同样的原因,我使用了以下代码,它工作得很好:

          an_array = [x for x in xyz if x not in a]
          

          这一行是程序的一部分!这意味着 XYZ 是一个要预先定义和分配的数组,也是变量 a

          使用生成器表达式(在所选答案中推荐)会带来一些困难,因为结果不是数组

          【讨论】: