结合FOR循环和IF语句的Python方法


266

我知道如何在单独的行上同时使用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])

但是,我找不到任何地方(复制和学习)的好例子,展示了一组复杂的命令(不仅是“ print x”),这些命令是在for循环和某些if语句组合后发生的。我期望的是:

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

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


23
就是这样...不要通过简化来简化事情。Pythonic并不意味着避免每个显式的for循环和if语句。
菲利克斯·克林

2
您可以在for循环中使用列表推导中生成的列表。那看起来就像您的最后一个例子。
雅各布

那么进入处理过程,如果if语句排除了已经匹配的值并且列表在for循环的迭代过程中不断增长,那么将for循环与if语句组合在一起的最快方法是什么?
ChewyChunks,2011年

3
@耐嚼,适当的数据结构将使代码更快,而不是语法糖。例如,x in a如果a是列表,则速度较慢。
Nick Dandoulakis 2011年

1
这是Python,一种解释性语言;为什么有人讨论代码的速度有多快?
ArtOfWarfare 2013年

Answers:


323

您可以使用以下生成器表达式

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

for x in gen:
    print x

1
gen = (y for (x,y) in enumerate(xyz) if x not in a)12当我键入时返回>>> ,for x in gen: print x为什么用枚举来表示意外行为?
ChewyChunks,2011年

9
可能,但是比for和if块原始效果更好。
Mike Graham

1
@ChewyChunks。那会起作用,但是调用枚举是多余的。
Johnsyweb 2011年

132
我真的很想念python能够说出的话for x in xyz if x:
bgusach 2014年

10
for x in (x for x in xyz if x not in a):为我工作,但为什么你不应该做for x in xyz if x not in a:,我不确定...
Matt Wenham

34

按照《 Python的禅宗》(如果您想知道代码是否是“ Pythonic”,那就去吧):

  • 美丽胜于丑陋。
  • 显式胜于隐式。
  • 简单胜于复杂。
  • 扁平比嵌套更好。
  • 可读性很重要。

获得两个s 的Pythonic方法是:sorted intersectionset

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

或那些xyz不在中的元素a

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

但是对于更复杂的循环,您可能希望通过迭代名称良好的生成器表达式和/或调出名称良好的函数来使其扁平化。试图将所有内容都放在一条线上很少是“ Pythonic”的。


在对您的问题和已接受的答案进行其他评论后进行更新

我不确定您要使用的是什么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

听起来像下面的评论,我应该研究发电机。我没用过 谢谢。生成器的速度是否快于FOR和IF语句的等效组合?我也使用过集合,但是有时列表中的多余元素是我无法舍弃的信息。
ChewyChunks,2011年

@ChewyChunks:生成器不是成为Pythonic的唯一方法!
Johnsyweb 2011年

3
@Johnsyweb,如果要引用Python的Zen:“应该有一种-最好只有一种-显而易见的方法。”
Wooble 2011年

@Wooble:应该有。我在同一时间回答另一个问题时引用了该部分!
Johnsyweb 2011年

18

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

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,则可以使用部分函数应用程序并使用运算符模块(该模块提供大多数运算符的功能)。

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)))

4
filter(a.__contains__, xyz)。通常,当人们使用lambda时,他们确实需要更简单的东西。
Veky

我想你误会了。__contains__是与其他方法一样的方法,只是它是一种特殊方法,这意味着它可以由操作员间接调用(in在这种情况下)。但是它也可以直接调用,它是公共API的一部分。专用名称被专门定义为最多具有一个下划线,以提供特殊方法名称的例外-并且在类范围内按词法进行命名时,它们会受到名称修饰的影响。请参阅docs.python.org/3/reference/datamodel.html#specialnamesdocs.python.org/3.6/tutorial/classes.html#private-variables
2016年

当然可以,但是两次导入只是为了能够引用仅使用属性即可访问的方法,这似乎很奇怪(通常,在需要双重分派的情况下使用运算符,但使用in正确的操作数单独分派)。此外,请注意,operatorcontains以名称导出方法__contains__,因此它肯定不是私有名称。我认为您只需要学会忍受一个事实,那就是并非每个双下划线都意味着“远离”。:-]
Veky

我认为您lambda需要解决的问题包括notlambda w: not w in a, xyz
javadba

过滤器似乎更优雅,特别是对于复杂的条件,这些条件将成为已定义的函数而不是lambda,命名lambda函数可能会增加一些可读性。当迭代元素对列表项进行某些修改时,生成器似乎会更好
Khanis Rok

16

以下是接受的答案的一种简化/一种解释:

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;)中的括号中对此进行了测试)


10

我可能会使用:

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

@KirillTitov是的,python是一种基本的非功能性语言(这是纯粹的命令性编码-我同意这个答案的作者的观点,这是设置python的编写方式。尝试使用功能性会导致读取效果差或无法pythonic我可以使用我使用的所有其他语言(scala,kotlin,javascript,R,swift等)进行功能编码,但是在python中很难/笨拙
javadba

9
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])

非常Zen,@ lazyr,但不会帮助我改进一个复杂的代码块,该代码块依赖于遍历一个列表并忽略另一个列表中的匹配元素。将第一个列表视为集合并将第二个联合/差异与不断增长的“忽略”列表进行比较会更快吗?
ChewyChunks,2011年

试试这个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
Kracekumar 2011年

@ChewyChunks如果列表中的任何一个在迭代过程中发生更改,则可能会根据忽略列表检查每个元素,这可能会更快-除非您应将其设置为忽略集。检查集合中的成员资格非常快:if x in ignore: ...
Lauritz V. Thaulow

@lazyr我只是使用忽略列表上的忽略集重写了我的代码。似乎处理时间要慢得多。(为公平起见,我正在比较使用if set(a) - set(ignore) == set([]):它的原因,也许这就是为什么它比检查成员资格要慢得多的原因。我将在以后的示例中通过比我正在编写的示例更简单的方式再次对其进行测试
。– ChewyChunks

5

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

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

for x in gen():
    print x

这对我来说更有用。我从没看过发电机。它们听起来很吓人(因为我在使用它们时通常很难理解的模块中看到了它们)。
ChewyChunks,2011年

2

使用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)))
  • junction_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是你的答案


2

我喜欢Alex的答案,因为过滤器恰好是应用于列表的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方法似乎更好,但是可以对所述成员进行修改,这似乎更适合生成器

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)

0

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

a = [1,2,3]
b = [3,6,2]
for both in set(a) & set(b):
    print(both)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.