繁体   English   中英

Python += 与 .extend() 在全局变量的函数内

[英]Python += versus .extend() inside a function on a global variable

我已经阅读了其他一些 SO( PythonScopeglobals 不需要 global )但似乎没有像我想要的那样明确解释,而且我在精神上难以筛选PyDocs是否告诉我我的问题的答案:

myList = [1]

def foo():
    myList = myList + [2, 3]
def bar():
    myList.extend([2, 3])
def baz():
    myList += [2, 3]

现在,可以理解的是,

>>> foo()
UnboundLocalError: local variable 'myList' referenced before assignment

bar()  # works
myList # shows [1, 2, 3]

但是之后

>>> baz()
UnboundLocalError: local variable 'myList' referenced before assignment

然而,我认为像+=这样的东西隐式调用了方法操作符,在这种情况下是extend() ,但错误意味着由于某种原因它实际上并没有将+=视为extends() 这与 Python 解析的工作方式一致吗?

我原以为调用与方法运算符等效的函数,它们在所有情况下都是等效的。 相反,它似乎将+=视为实际的赋值运算符。 除此之外,这并不完全正确,因为如果我做某事(无可否认是人为的):

myList = range(50000000) # wait a second or two on my laptop before returning
myList += [0]            # returns instantly
myList = myList + [1]    # wait a second or two before returning

所有这些都是预期的,如果+=实际上只是调用了extend()

是否有一些更细微的区别(或非常明显的点......)我遗漏了这清楚表明baz()中的myList需要被视为局部变量,因此+=不能隐式转换为extend()这样它就可以识别全局变量?

+=不会隐式调用extend() 首先,它是一个增广赋值运算符

如果您查看assignment部分,它会说:

将对象分配给单个目标的递归定义如下。

如果目标是标识符(名称):

如果名称未出现在当前代码块的全局语句中:名称绑定到当前本地命名空间中的对象。 否则:名称绑定到当前全局命名空间中的对象。

由于增广分配是:

增广赋值是在单个语句中组合二元运算和赋值语句:

它遵循相同的规则。 如你看到的:

>>> def baz():
        myList += [2, 3]


>>> dis.dis(baz)
  2           0 LOAD_FAST                0 (myList)
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 (3)
              9 BUILD_LIST               2
             12 INPLACE_ADD         
             13 STORE_FAST               0 (myList)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE  

扩充赋值计算目标(与普通赋值语句不同,它不能是解包语句)和表达式列表,对两个操作数执行特定于赋值类型的二元运算,并将结果分配给原始目标。 目标只评估一次..

第一次调用尝试评估myList ,这导致LOAD_FAST因为没有global语句它被假定为局部变量:

LOAD_FAST(var_num)

本地co_varnames[var_num]的引用co_varnames[var_num]入堆栈。

找不到它,因此引发了错误。 如果它找到,那么我们会得到 oppcode INPLACE_ADD ,它调用myList.__iadd__方法myList.__iadd__完成扩展工作,一旦这个操作完成,结果将被分配回变量,但我们永远不会myList.__iadd__myList.__iadd__

无论如何,您不应该真正操作global s,从您的函数返回新结果或将其作为参数传递。

当你改变列表时,你应该说 global myList。 通过 mutate 我的意思是改变引用。 第一个示例和第三个示例基本相同,您只需使用 += 进行速记

myList = [1]

def foo():
    global myList
    myList = myList + [2, 3]
def bar():
    myList.extend([2, 3])
def baz():
    global myList
    myList += [2, 3]

foo()
bar()
baz()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM