2012-09-30 125 views
14

我正在写一个解析器,并在调试它的过程中,我发现,很显然,这是合法的Python:这个Python语句是什么意思?

for [] in [[]]: print 0 

,所以是这个(!):

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

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

这句话究竟是什么意思?

+0

谷歌的序列拆封。应该解释它。 – hendrik

回答

14

在执行方面:没什么。

for循环本身循环遍历空列表,因此不会发生迭代。

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

该声明是合法的,因为target token 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 
+0

啊,这就解释了为什么'[] []]:print 0'中的[] []有效,但[[]]:print 0'中的[] [:]怎么办?那是合法的/这是什么意思? – Mehrdad

+2

该列表不是空的。它包含一个元素,它是空的列表。因此,0将被打印。 –

+0

@Mehrdad:左侧的'target_list'可以是一个切片分配。 'alist [1:2]'= somelist'用'somelist'的值替换'[1:2]'指示的片段。因此,切片是一个有效的左侧表达式,并且在循环中也是有效的。 –

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() i s 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' 
1

这是我最好的猜测:

for [] in [[]]手段“为[]每个实例(空列表对象)在此列表[[]](一个列表中只有一个元素,它是一个空列表对象),print 0

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

+0

注意:因为我没有在这台机器上访问Python,所以我将这个猜测基于Skulpt的结果,结果打印出'0'。我假设Skulpt(http://www.skulpt.org/)正在做CPython的同样的事情,在这个角落案例中这不一定是一个安全的假设。 –

1

的for..in结合结构在Python手册

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一次。

您正在阅读的解析器是否自动生成?这种说法可以由非人类来源产生。我没有看到它的目的。

+0

重要的部分是手册中的“target”原子,它定义了一个左手边的法律表达式。 –

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

意味着,在[[]]每个空iterables,那就是它包含一个空的列表清单,打印0它不仅局限于名单,但每iterables可以投入这一点。例如,您可以尝试:

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

并且它会打印0四次。

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

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

现在,在你的第一个例子,你迭代一个包含一个空列表的列表。这意味着您正试图打印尽可能多的0 s,因为此列表中有[] 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 

所以,当你调用[][:],你正在做的是做一个新的空单,其工作一样我解释你的第一个例子