【问题标题】:Why do tuples in a list comprehension need parentheses? [duplicate]为什么列表理解中的元组需要括号? [复制]
【发布时间】:2018-06-21 13:30:31
【问题描述】:

众所周知,元组不是用括号定义的,而是用逗号定义的。 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')]

【问题讨论】:

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


    【解决方案1】:

    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) 解析器不会回溯。因此,模棱两可的语法是被禁止的。

    【讨论】:

    【解决方案2】:

    因为我觉得"because the grammar forbids it" 有点太刻薄了,所以我想出了一个原因

    它开始将表达式解析为列表/集合/元组,并期待 , 并遇到 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      ''     
    

    【讨论】:

      【解决方案3】:

      没有引发此限制的解析器问题。与 Silvio Mayolo 的回答相反,LL(1) 解析器可以很好地解析无括号语法。在原始列表理解补丁的早期版本中,括号是可选的;它们只是为了使含义更清晰而被强制使用。

      在 2000 年引用 Guido van Rossum 的话,在 response 中,有人担心 [x, y for ...] 会导致解析器问题,

      别担心。 Greg Ewing 在 Python 中表达这一点没有问题 自己的语法,就像解析器一样受到限制。 (它的 LL(1),相当于纯递归下降 前瞻令牌,即没有回溯。)

      这是 Greg 的语法:

      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)]
      

      在列表理解补丁的早期版本上,它工作得很好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-01
        • 2017-09-08
        • 1970-01-01
        • 2013-04-07
        • 2014-08-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多