为什么元组需要列表理解中的parantheses

时间:2018-01-12 05:47:48

标签: python python-3.x python-2.7 tuples list-comprehension

众所周知,元组不是用括号定义的,而是用逗号定义的。 Quote from documentation

  

元组由许多以逗号分隔的值组成

因此:

myVar1 = 'a', 'b', 'c'
type(myVar1)
# Result:
<type 'tuple'>

另一个引人注目的例子是:

myVar2 = ('a')
type(myVar2)
# Result:
<type 'str'>  

myVar3 = ('a',)
type(myVar3)
# Result:
<type 'tuple'>

即使是单元素元组也需要逗号,并且总是使用括号来避免混淆。 我的问题是:为什么我们不能在列表推导中省略数组的括号?例如:

myList1 = ['a', 'b']
myList2 = ['c', 'd']

print([(v1,v2) for v1 in myList1 for v2 in myList2])
# Works, result:
[('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd')]

print([v1,v2 for v1 in myList1 for v2 in myList2])
# Does not work, result:
SyntaxError: invalid syntax

不是第二个列表理解只是以下循环的语法糖,这确实有效吗?

myTuples = []
for v1 in myList1:
    for v2 in myList2:
        myTuple = v1,v2
        myTuples.append(myTuple)
print myTuples
# Result:
[('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd')]

3 个答案:

答案 0 :(得分:12)

Python的语法是LL(1),这意味着它在解析时只向前看一个符号。

[(v1, v2) for v1 in myList1 for v2 in myList2]

在这里,解析器看到类似的东西。

[ # An opening bracket; must be some kind of list
[( # Okay, so a list containing some value in parentheses
[(v1
[(v1,
[(v1, v2
[(v1, v2)
[(v1, v2) for # Alright, list comprehension

然而,如果没有括号,它必须在早些时候作出决定。

[v1, v2 for v1 in myList1 for v2 in myList2]

[ # List-ish thing
[v1 # List containing a value; alright
[v1, # List containing at least two values
[v1, v2 # Here's the second value
[v1, v2 for # Wait, what?

回溯往往比较慢的解析器,因此LL(1)解析器不会回溯。因此,禁止使用含糊不清的语法。

答案 1 :(得分:2)

当我觉得"because the grammar forbids it"有点过于苛刻时,我提出了理由

它开始将表达式解析为list / set / tuple,并期待,,而是遇到for令牌。

例如:

$ python3.6 test.py
  File "test.py", line 1
    [a, b for a, b in c]
            ^
SyntaxError: invalid syntax

标记如下:

$ python3.6 -m tokenize test.py
0,0-0,0:            ENCODING       'utf-8'        
1,0-1,1:            OP             '['            
1,1-1,2:            NAME           'a'            
1,2-1,3:            OP             ','            
1,4-1,5:            NAME           'b'            
1,6-1,9:            NAME           'for'          
1,10-1,11:          NAME           'a'            
1,11-1,12:          OP             ','            
1,13-1,14:          NAME           'b'            
1,15-1,17:          NAME           'in'           
1,18-1,19:          NAME           'c'            
1,19-1,20:          OP             ']'            
1,20-1,21:          NEWLINE        '\n'           
2,0-2,0:            ENDMARKER      ''     

答案 2 :(得分:1)

没有引发该限制的解析器问题。与Silvio Mayolo的答案相反,LL(1)解析器本来可以解析无括号语法。在原始列表理解补丁的早期版本中,括号是可选的;他们只是为了使含义更清楚而被强制执行。

早在2000年,Guido van Rossum在response中向担心[x, y for ...]会导致解析器问题的人报价,

不用担心。格雷格·尤因(Greg Ewing)毫无疑问地在Python 自己的语法,这几乎和解析器一样严格。 (它的 LL(1),等效于具有一个的纯递归下降 前瞻令牌,即无回溯。)

这是格雷格的语法:

atom: ... | '[' [testlist [list_iter]] ']' | ...
  list_iter: list_for | list_if
  list_for: 'for' exprlist 'in' testlist [list_iter]
  list_if: 'if' test [list_iter]

请注意,之前的列表语法为'[' [testlist] ']'。让我来 用不同的术语解释它:

解析器解析一系列逗号分隔的表达式。先前, 它原以为']'是紧随其后的唯一可能的令牌。 更改之后,'for'是另一个可能的跟随标记。这个 知道如何解析匹配的解析器根本没有问题 括号!

如果您不支持[x, y for ...],因为它模棱两可 (对于人类读者,而不是解析器!),我们可以更改语法 像这样:

'[' test [',' testlist | list_iter] ']'

(请注意,|的绑定少于串联,而[...]的含义是 可选部分。)

另请参阅线程中的next response,Greg Ewing运行所在的线程

>>> seq = [1,2,3,4,5]
>>> [x, x*2 for x in seq]
[(1, 2), (2, 4), (3, 6), (4, 8), (5, 10)]

在列表理解补丁的早期版本中,效果很好。