有两个变量的“for循环”?

165

我该如何在同一个 for 循环中包含两个变量?

t1 = [a list of integers, strings and lists]
t2 = [another list of integers, strings and lists]

def f(t):  #a function that will read lists "t1" and "t2" and return all elements that are identical
    for i in range(len(t1)) and for j in range(len(t2)):
        ...

4
只有当可迭代对象的长度相同时,“zip”才是有效的。 - kojiro
6
你是想要在一行中嵌套循环,还是只是同时迭代列表? - SethMMorton
5
如果SethMMorton的问题不太清楚,请注意:如果xy均为3,则同时(也称为“锁步”或“并行”)迭代将给出0, 0,然后是1, 1,最后是2, 2; 嵌套迭代将依次得到0, 00, 10, 21, 01, 11, 22, 02, 12, 2。(或者您是否需要与这两种方法都不同的结果?如果是,请说明您需要什么。) - abarnert
3
我没有点踩,但可能是因为在评论者要求澄清后,仍然不清楚你需要同时循环还是嵌套循环。 - SethMMorton
你知道吗,有一种更简单的方法可以获取两个可迭代对象中相同的成员。请参考:http://docs.python.org/3.3/library/stdtypes.html?highlight=frozenset#set - kojiro
显示剩余6条评论
8个回答

258

如果您想要嵌套循环的效果,请使用:

import itertools
for i, j in itertools.product(range(x), range(y)):
    # Stuff...

如果您只想同时循环,请使用:

for i, j in zip(range(x), range(y)):
    # Stuff...
请注意,如果xy的长度不同,zip将截断到最短的列表。正如@abarnert指出的那样,如果您不想截断到最短的列表,可以使用itertools.zip_longest
更新
根据请求,“需要一个函数读取“t1”和“t2”列表,并返回所有相同的元素”,我认为问题不是要使用zipproduct,而是要使用set
def equal_elements(t1, t2):
    return list(set(t1).intersection(set(t2)))
    # You could also do
    # return list(set(t1) & set(t2))

setintersection方法将返回所有与另一个集合共有的元素(请注意,如果您的列表包含其他的list,您可能需要先将内部的list转换为tuple,以使它们是可哈希的;否则对set的调用将失败)。然后,list函数将该集合转换回列表。

更新2

或者,原始问题可能想要在列表中相同位置上的元素相同。在这种情况下,zip将是最合适的,并且它截断为最短的列表是您想要的(因为当其中一个列表只有5个元素时,在索引9处不可能有相同的元素)。如果这正是您想要的,请使用以下代码:

def equal_elements(t1, t2):
    return [x for x, y in zip(t1, t2) if x == y]
这将返回一个仅包含在列表中相同位置上相同元素的列表。

4
“zip”的答案不够资格。正如我在上面评论的那样,只有在x == y的情况下才有效。 - kojiro
2
@kojiro:嗯,OP没有说明如果x != y会发生什么。一旦他解释了,答案要么是zip,要么是itertools.zip_longest - abarnert
有没有不使用zip的同时迭代? - Quester
3
您可以使用itertools.izip,这本质上是相同的。除此之外,据我所知没有其他的方法。但是,zip有什么问题吗? - SethMMorton
@Quester zip 可以用于两个不同的 x 和 y,你只需要知道它的行为方式(就像我在我的回答中所解释的那样)。但是,根据您的编辑,我不确定您是否想要使用 zip - SethMMorton
显示剩余7条评论

100

这里可能有两个问题:如何同时迭代这些变量,或者如何循环它们的组合。

幸运的是,两个问题都有简单的答案。对于第一种情况,您需要使用zip

x = [1, 2, 3]
y = [4, 5, 6]

for i, j in zip(x, y):
   print(str(i) + " / " + str(j))

将输出

1 / 4
2 / 5
3 / 6

记住你可以将任何可迭代对象放入zip中,所以你也可以像下面这样轻松写出示例代码:

Remember that you can put any iterable in zip, so you could just as easily write your exmple like:

for i, j in zip(range(x), range(y)):
    # do work here.

实际上,我刚刚意识到这样做行不通。它只会迭代直到较小的范围用完。在这种情况下,听起来你想要迭代循环的组合。

在另一种情况下,你只需要一个嵌套循环。

for i in x:
    for j in y:
        print(str(i) + " / " + str(j))

给你

1 / 4
1 / 5
1 / 6
2 / 4
2 / 5
...

你也可以使用列表推导式来完成这个操作。

[str(i) + " / " + str(j) for i in range(x) for j in range(y)]

20

你有什么理由不能使用嵌套的for循环?

for i in range(x):
   for j in range(y):
       #code that uses i and j

11
@Matt:对我来说,它看起来不够优雅。 - Quester
7
@Quester:您可以像SethMMorton的回答中那样使用product,这样如果您有六个范围,或者它们的数量在运行时动态确定,那么这种方法肯定更加优雅/易读。但如果只有两个范围...那么要想简单明了还是循环嵌套最好了。 - abarnert
谁不喜欢缩进所有内容以让Python高兴呢? - mohbandy
5
嵌套循环运行i*j次。如果他想只运行i次,考虑i == j怎么办? - QuestionEverything
@abarnert "> 用两个嵌套循环来实现简单性是很难被超越的。" 我曾经在一个项目上工作,需要我打几百次 "for i in range(len(...)): for j in rnage(len((...))" ... 如果我知道这个方法,那么我可以使用 "for (i,j) in grid:" 来节省很多时间。 - Kartik

18
for (i,j) in [(i,j) for i in range(x) for j in range(y)]

应该去做它。


12
这只是Matt Quinlan回答中嵌套的for循环的更难读的变体。 - abarnert
3
好像是梦中梦 :) - lionelmessi
@abarnert 不是的:它能够在单行中完成问题的精神工作。 - WestCoastProjects

9

如果您只需要对一定范围进行固定步长的迭代,有几种方法可以实现:

for i in range(x):
  j = i
  …
# or
for i, j in enumerate(range(x)):
  …
# or
for i, j in ((i,i) for i in range(x)):
  …

以上所有内容都等同于 for i, j in zip(range(x), range(y)),如果 x <= y
如果您想要一个嵌套循环,并且只有两个可迭代对象,请使用嵌套循环:
for i in range(x):
  for i in range(y):
    …

如果你有超过两个可迭代对象, 请使用itertools.product

最后,如果你想要锁定到x然后继续到y的迭代,你需要决定x值的其余部分应该是什么。

for i, j in itertools.zip_longest(range(x), range(y), fillvalue=float('nan')):
  …
# or
for i in range(min(x,y)):
  j = i
  …
for i in range(min(x,y), max(x,y)):
  j = float('nan')
  …

8

"Python 3."

使用zip和range,在for循环中添加2个变量;返回一个列表。

注意:只会运行到最小范围结束。

>>>a=[g+h for g,h in zip(range(10), range(10))]
>>>a
>>>[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Python文档链接!https://docs.python.org/3.3/library/stdtypes.html#iterator.__iter__ - Open-Business

6

针对您的使用情况,可能更容易利用一个 while 循环。

t1 = [137, 42]
t2 = ["Hello", "world"]

i = 0
j = 0
while i < len(t1) and j < len(t2):
    print t1[i], t2[j]
    i += 1
    j += 1

# 137 Hello
# 42 world

作为警告,这种方法将截断为您最短列表的长度。

1
如果您需要同步两个(或更多)数组中的位置,则这是最佳解决方案。 - J-a-n-u-s
1
笨拙的/不优雅的 - WestCoastProjects

3
我想您正在寻找嵌套循环。
示例(基于您的编辑):
t1=[1,2,'Hello',(1,2),999,1.23]
t2=[1,'Hello',(1,2),999]

t3=[]

for it1, e1 in enumerate(t1):
    for it2, e2 in enumerate(t2):
        if e1==e2:
            t3.append((it1,it2,e1))

# t3=[(0, 0, 1), (2, 1, 'Hello'), (3, 2, (1, 2)), (4, 3, 999)]

可以简化为一个推导式:
[(it1,it2,e1) for it1, e1 in enumerate(t1) for it2, e2 in enumerate(t2) if e1==e2] 

但是要找到共同点,你只需执行以下操作:
print set(t1) & set(t2)
# set([(1, 2), 1, 'Hello', 999])

如果您的列表包含非可哈希对象(如其他列表,字典),请使用冻结集合:

from collections import Iterable
s1=set(frozenset(e1) if isinstance(e1,Iterable) else e1 for e1 in t1)
s2=set(frozenset(e2) if isinstance(e2,Iterable) else e2 for e2 in t2)
print s1 & s2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接