Python中嵌套列表推导和嵌套生成器表达式的顺序

6

我刚接触Python,对Python官方文档中的一段代码感到困惑。

unique_words = set(word  for line in page  for word in line.split())

对我来说,它看起来相当于:
unique_words=set()
for word in line.split():
    for line in page:
        unique_words.add(word)

在嵌套循环中,如何在第一个循环之前使用line变量,而它又未被定义?然而,它确实可以工作。我认为这表明了嵌套列表推导和生成器表达式的顺序是从左到右的,这与我的先前理解相矛盾。

有人能为我澄清正确的顺序吗?


1
你的循环顺序颠倒了。for line in page 部分应该是外层循环。 - APerson
如果您认为您的嵌套循环是等效的,您需要解释外部循环中的“line”在哪里定义。嵌套生成器表达式中的顺序与任何嵌套循环相同。 - chepner
1
请参考以下链接:https://dev59.com/LmIk5IYBdhLWcg3wNru8 - wim
6个回答

7

word for line in page for word in line.split()

这部分的工作原理如下:

for line in page:
    for word in line.split():
        print word

() 这使得它成为生成器函数,因此整个语句的工作方式如下:

def solve():
    for line in page:
        for word in line.split():
            yield word

而 set() 用于避免重复单词,因为代码旨在获取“唯一单词”。


很好的答案;我想补充一点,集合用于删除重复项。 - user3378649
(word for line in page for word in line.split()) - BreakBadSP

2
从官方文档的教程中可知:
列表推导式由包含表达式的括号、后跟零个或多个for或if子句的形式组成。结果将是通过在其后跟的for和if子句的上下文中评估表达式而产生的新列表。例如,如果它们不相等,则此列表组合了两个列表的元素:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
并且它等价于:
>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
请注意,这两个片段中for和if语句的顺序相同。
请参见上面引用的最后一句话。
还要注意,您描述的结构在官方上并没有被称为“嵌套列表推导式”。嵌套列表推导式包括一个列表推导式,它位于另一个列表推导式中,例如(再次引用教程):
[[row[i] for row in matrix] for i in range(4)]

你所询问的是一个具有多个for子句的列表推导式。

请注意,第一个示例并没有回答问题:xy是独立的,可以交换,但这在OP的示例中并不适用。 - Eric Duminil
1
@EricDuminil - 这确实回答了问题。OP想知道在理解式中解析多个for子句的正确顺序。实际上,OP已经通过观察行为推断出了正确的顺序,但是希望得到确认。有什么比官方文档更好的确认呢?无论xy是否独立都不重要。相关部分是将理解式展开为其等效的嵌套循环形式,这恰好是被接受并获得最高票数的答案所做的(除了该答案没有引用任何参考文献来证明它)。 - John Y
我仍然认为这是一个糟糕的示例(在文档中,而不是你的答案),因为xy可以交换。它并没有真正涵盖OP所述的情况,即单词在行中,行在页中。 - Eric Duminil
@EricDuminil - 我明白你的意思,但关键是它确实涵盖了OP的情况,因为循环的顺序很重要。请注意,虽然x和y是独立的,但它们并不相等。因此,如果你交换它们,你会得到不同的结果。你似乎在说,在OP的例子中,如果顺序错了,程序就会崩溃。当然,如果教程示例恰好在一个不能处理左侧元素为“4”的元组的程序中,那么将其弄错也会导致程序崩溃。如果你理解了教程示例,你就能理解如何解析OP的代码片段。 - John Y

1

你的循环有误。请使用以下代码:

unique_words = set(word for line in page for word in line.split())
print unique_words

l = []
for line in page:
    for word in line.split():
        l.append(word)
print set(l)

输出:

C:\...>python test.py
set(['sdaf', 'sadfa', 'sfsf', 'fsdf', 'fa', 'sdf', 'asd', 'asdf'])
set(['sdaf', 'sadfa', 'sfsf', 'fsdf', 'fa', 'sdf', 'asd', 'asdf'])

他是对的!我应该使用集合而不是列表。这是删除重复值的一种方法。 - user3378649
1
请解释一下为什么要给我点踩,这样我才能改进我的回答。 - Vincent Beltman

0

你的嵌套循环混乱了。代码实际上是这样做的:

unique_words={}
for line in page:
    for word in line.split():
        unique_words.add(word)

0
for outer_val in outer_loop :
    for inner_val in inner_loop:
        do_something()

翻译成 [在外部循环中遍历 outer_loop,内部循环中遍历 inner_loop 执行 do_something()]

[ op <inner_loop> <outer_loop>]


-2
除了强调顺序的正确答案之外,我还要补充一点,我们使用set来从行中删除重复项,以制作“唯一单词”。请查看thisthis线程。
unique_words = set(word for line in page for word in line.split())
print unique_words

l = {}
for line in page:
    for word in line.split():
        l.add(word)
print l

1
{} 不会创建一个空集合。 - Mad Physicist

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