14

我正在写一个解析器,在调试它的过程中,我发现显然,这是合法的 Python:

for []    in [[]]: print 0

这也是(!):

for [][:] in [[]]: print 0

我不怪解析器感到困惑......无法弄清楚如何解释它!

这句话究竟是什么意思?

4

6 回答 6

14

在执行方面:没有。

循环本身在一个空列表上for循环,因此不会发生迭代。

这是一件好事,因为这for []意味着:将循环中的每个条目分配给 0 个变量。后半部分可能是让你困惑的地方。

该语句是合法的,因为目标标记token_list允许您将序列中的值分配给同样大的变量名序列;我们称之为元组解包。以下是在分配和删除中更有用的目标列表示例:

(a, b, c) = range(3)
del a, b, c

您可以在for循环中执行相同的操作:

nested = [[1,2,3], [4,5,6]]
for a, b, c in nested:
    print nested

您可以同时使用元组和列表作为target_list令牌,这也是合法的:

[a, b] = (1, 2)

但是,在 Python 中,列表可以为空。因此,以下内容是合法的,但毫无意义:

[] = []

最后,这是这样的:

nested_empty = [[], [], []]
for [] in nested_empty:
    pass

目标列表更有趣:

[][:] = [1, 2, 3]

现在左侧在分配中使用切片。从文档中:

如果目标是切片:评估引用中的主要表达式。它应该产生一个可变的序列对象(例如列表)。分配的对象应该是相同类型的序列对象。接下来,评估下限和上限表达式,只要它们存在;默认为零和序列的长度。边界应评估为(小)整数。如果任一边界为负,则将序列的长度添加到其中。生成的边界被剪裁为介于零和序列长度之间(含)。最后,要求序列对象将切片替换为指定序列的项目。如果对象允许,切片的长度可能与指定序列的长度不同,因此会改变目标序列的长度。

所以这里我们不再使用元组解包;相反,我们将左侧列表的一部分替换为右侧列表。但是因为在我们的示例中,左侧列表是匿名列表文字,所以结果更改的列表再次丢失。

但是因为这样的赋值在 for 循环中也是合法的,所以以下是合法的语法,尽管相当荒谬:

for [][:] in [range(i) for i in range(10)]: print 0
于 2012-09-30T08:26:56.403 回答
1
for [] in [[]]: print 0

它相当于:

In [44]: for [x,y] in [[1,2],[3,4],[5,6]]: # or even (x,y) will work
    print x,y
   ....:     
   ....:     
1 2
3 4
5 6

但前者期望列表中没有返回值,即列表中的值要么为空,要么len()0

你不能()在那里使用,因为它不是有效的。

因为在 python 中,你也可以像这样分配:

In [56]: x,y=[1,2]      #this means that the expression on RHS should return two values
                        # (x,y)=[1,2] or x,y=(1,2) or (x,y)=(1,2) all are valid

In [57]: x
Out[57]: 1

In [58]: y
Out[58]: 2


In [62]: x,y='ab'     #assign a to x and b to y

In [63]: x
Out[63]: 'a'

In [64]: y
Out[64]: 'b'
于 2012-09-30T08:27:57.667 回答
1

这是我最好的猜测:

for [] in [[]][]意思是“对于这个列表[[]](一个只有一个元素的列表,它是一个空列表对象)中(一个空列表对象)的每个实例, print 0.

在第二种情况下,我认为[:]只会slice()使用所有默认值进行调用,这只会占用整个列表的一部分。在内部这可能会做一些事情,例如制作列表对象的副本,但在这种情况下的效果应该是相同的。

于 2012-09-30T08:28:07.617 回答
1

Python 手册中描述了 for..in 结构

http://docs.python.org/reference/compound_stmts.html#the-for-statement

in关键字左侧可以有多个变量

for [i,j] in [(1,2),(3,4),(5,6)]:
  print i, j

for [i,j] in [[1,2],[3,4],[5,6]]:
  print i, j

手册说它被解释为

i,j = (1,2)

第一次迭代等等。因此,您可以有一个空的变量列表,因为迭代的列表只有一个空列表作为元素。此循环将打印 0 一次。

您正在阅读的解析器是自动生成的吗?这种陈述可以由非人类来源生成。我看不出它的目的。

于 2012-09-30T08:29:35.810 回答
1
for []    in [[]]: print 0

表示对于 [[]] 中的每个空迭代器,即包含一个空列表的列表,打印 0。它不仅限于列表,每个迭代器都可以放入其中。例如,您可以尝试:

# empty list, empty tuple, empty string, empty unicode
for [] in [[], (), '', unicode()]: print 0

它会打印 0 四次。

[][:] 与 [] 相同。它将返回一个空列表,所以我的答案与上面相同。

于 2012-09-30T08:37:43.883 回答
0

假设您有一个如下所示的元组列表:

L = [(1,2), (3,4), (5,6)]

假设您想以某种特殊方式打印这些元组:

for tup in L:
    a = tup[0]
    b = tup[1]
    if a<b:
        print a,b
    else:
        print b,a

但是分配ab明确地成为的内容tup是相当乏味的。所以你可以这样做:

for tup in L:
    a,b = tup
    if a<b:
        print a,b
    else:
        print b,a

但你可以让它变得不那么乏味:

for (a,b) in L: # you could also do "for [a,b] in L"
    if a<b:
        print a,b
    else:
        print b,a

在这里,(a,b)模式匹配迭代返回的元素。在 for 循环的第一次执行中,迭代返回的元素是,它与(1,2)模式匹配(a,b),因此分配1a2b

现在,在您的第一个示例中,您正在迭代一个包含空列表的列表。这意味着您正在尝试打印与此列表中的 s 一样多0的 s []。但它比这更复杂一点:

当您尝试像我的第三个示例中那样进行模式匹配时,python 会迭代变量列表(或元组)和迭代器返回的元素,同时在它们进行时分配值。因此,当您的模式不包含变量时,您尝试进行模式匹配的元素也应该是空的(并且是可迭代的)。这解释了以下行为:

>>> for i in 5: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

>>> for [] in [[], 5]: print 0
... 
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

……还有这种行为:

>>> x,y = (2,5,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

>>> for [] in [[], [5]]: print 0
... 
0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

现在,对于您的第二个示例,该[:]操作基本上创建了它所调用的列表的副本,因此更改原始列表不会更改副本,反之亦然:

>>> L = [1,2,3]
>>> M = L
>>> M[0] = 'a'
>>> print L[0]
'a'

>>> L = [1,2,3]
>>> M = L[:]
>>> M[0] = 'a'
>>> print L[0]
1

因此,当您致电时[][:],您所做的只是创建一个新的空列表,这与我对您的第一个示例的解释相同

于 2012-09-30T08:41:50.650 回答