【问题标题】:Why isn't assigning to an empty list (e.g. [] = "") an error?为什么分配给空列表(例如 [] = "")不会出错?
【发布时间】:2023-03-17 10:39:02
【问题描述】:

在 python 3.4 中,我正在输入

[] = "" 

它工作正常,没有引发异常。虽然当然[] 之后不等于""

[] = ()

也可以正常工作。

"" = []

按预期引发异常,

() = ""

尽管按预期引发了异常。发生什么了?

【问题讨论】:

    标签: python syntax variable-assignment


    【解决方案1】:

    你不是在比较平等。您正在分配

    Python 允许您分配给多个目标:

    foo, bar = 1, 2
    

    将这两个值分别分配给foobar。您所需要的只是右侧的 sequenceiterable,左侧的名称列表或元组。

    当你这样做时:

    [] = ""
    

    您将一个 empty 序列(空字符串仍然是序列)分配给一个空的名称列表。

    本质上和做是一样的:

    [foo, bar, baz] = "abc"
    

    您以foo = "a"bar = "b"baz = "c" 结尾,但字符更少。

    但是,您不能分配给字符串,因此分配左侧的 "" 永远不会起作用,并且始终是语法错误。

    Assignment statements documentation

    赋值语句计算表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组)并将单个结果对象从左到右分配给每个目标列表。

    将对象分配给目标列表,可选地用括号或方括号括起来,递归定义如下。

    强调我的

    Python 不会为空列表抛出语法错误实际上是一个错误!官方记录的语法不允许空的目标列表,而对于空的(),您确实会收到错误消息。见bug 23275;它被认为是无害的错误:

    首先要认识到这已经存在了很长时间并且是无害的。

    另见Why is it valid to assign to an empty list but not to an empty tuple?

    【讨论】:

      【解决方案2】:

      它遵循文档中的Assignment statements section 规则,

      assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)
      

      如果target list是一个逗号分隔的目标列表:该对象必须是一个与目标列表中有相同数量的项目的可迭代对象,并且项目从左到右分配对,到相应的目标。

      对象必须是一个序列,其项目数与目标列表中的目标数量相同,并且项目从左到右分配给相应的目标。

      所以,当你说

      [] = ""
      

      "" 是一个可迭代对象(任何有效的 Python 字符串都是一个可迭代对象),并且它正在列表的元素上进行解包。

      例如,

      >>> [a, b, c] = "123"
      >>> a, b, c
      ('1', '2', '3')
      

      因为你有一个空字符串和一个空列表,所以没有什么可以解压的。所以,没有错误。

      但是,试试这个

      >>> [] = "1"
      Traceback (most recent call last):
        File "<input>", line 1, in <module>
      ValueError: too many values to unpack (expected 0)
      >>> [a] = ""
      Traceback (most recent call last):
        File "<input>", line 1, in <module>
      ValueError: need more than 0 values to unpack
      

      [] = "1" 的情况下,您试图将字符串"1" 解包到一个空的变量列表上。所以它抱怨“解包的值太多(预期为 0)”。

      同样,在[a] = "" 的情况下,你有一个空字符串,所以没有什么可以解包的,但是你将它解包到一个变量上,这又是不可能的。这就是为什么它抱怨“需要超过 0 个值才能解压”。

      除此之外,正如您所注意到的,

      >>> [] = ()
      

      也不会抛出错误,因为() 是一个空元组。

      >>> ()
      ()
      >>> type(())
      <class 'tuple'>
      

      当它在一个空列表上解包时,没有任何东西可以解包。所以没有错误。


      但是,当你这样做时

      >>> "" = []
        File "<input>", line 1
      SyntaxError: can't assign to literal
      >>> "" = ()
        File "<input>", line 1
      SyntaxError: can't assign to literal
      

      正如错误消息所说,您正在尝试分配给字符串文字。这是不可能的。这就是您收到错误的原因。就像说

      >>> 1 = "one"
        File "<input>", line 1
      SyntaxError: can't assign to literal
      

      内部

      在内部,这个赋值操作会被翻译成UNPACK_SEQUENCE操作码,

      >>> dis(compile('[] = ""', "string", "exec"))
        1           0 LOAD_CONST               0 ('')
                    3 UNPACK_SEQUENCE          0
                    6 LOAD_CONST               1 (None)
      

      这里,由于字符串为空,UNPACK_SEQUENCE 解包0 次。但是当你有这样的东西时

      >>> dis(compile('[a, b, c] = "123"', "string", "exec"))
        1           0 LOAD_CONST               0 ('123')
                    3 UNPACK_SEQUENCE          3
                    6 STORE_NAME               0 (a)
                    9 STORE_NAME               1 (b)
                   12 STORE_NAME               2 (c)
                   15 LOAD_CONST               1 (None)
                   18 RETURN_VALUE
      

      序列123 被解包到堆栈中,从右到左。所以,栈顶是1,下一个是2,最后一个是3。然后从栈顶开始,将左侧表达式中的变量一一赋值。


      顺便说一句,在 Python 中,这就是您可以在同一个表达式中执行多个赋值的方法。例如,

      a, b, c, d, e, f = u, v, w, x, y, z
      

      这是可行的,因为右手边的值被用来构造一个元组,然后它将被解包到左手边的值上。

      >>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
        1           0 LOAD_NAME                0 (u)
                    3 LOAD_NAME                1 (v)
                    6 LOAD_NAME                2 (w)
                    9 LOAD_NAME                3 (x)
                   12 LOAD_NAME                4 (y)
                   15 LOAD_NAME                5 (z)
                   18 BUILD_TUPLE              6
                   21 UNPACK_SEQUENCE          6
                   24 STORE_NAME               6 (a)
                   27 STORE_NAME               7 (b)
                   30 STORE_NAME               8 (c)
                   33 STORE_NAME               9 (d)
                   36 STORE_NAME              10 (e)
                   39 STORE_NAME              11 (f)
                   42 LOAD_CONST               0 (None)
                   45 RETURN_VALUE
      

      但是经典的交换技术a, b = b, a 使用栈顶元素的旋转。如果您只有两个或三个元素,则使用特殊的 ROT_TWOROT_THREE 指令处理它们,而不是构造元组和解包。

      >>> dis(compile('a, b = b, a', "string", "exec"))
        1           0 LOAD_NAME                0 (b)
                    3 LOAD_NAME                1 (a)
                    6 ROT_TWO
                    7 STORE_NAME               1 (a)
                   10 STORE_NAME               0 (b)
                   13 LOAD_CONST               0 (None)
                   16 RETURN_VALUE
      

      【讨论】:

      猜你喜欢
      • 2019-04-27
      • 1970-01-01
      • 2021-04-06
      • 2013-03-21
      • 2020-08-22
      相关资源
      最近更新 更多