当前位置: 首页 > 面试题库 >

Python:带有就地添加的LOAD_FAST与LOAD_DEREF

宋景福
2023-03-14
问题内容

上周五,我去了一次工作面试,不得不回答以下问题:为什么这段代码会引发异常(UnboundLocalError: local variable 'var' referenced before assignment在包含的行上var += 1)?

def outer():
    var = 1

    def inner():
        var += 1
        return var

    return inner

我不能给出正确的答案。这个事实真的让我感到不安,当我回到家时,我非常努力地寻找正确的答案。好吧,我 已经 找到了答案,但现在有别的东西混淆了我。

我必须事先 说,我的问题更多是关于设计语言时所做出的决定,而不是它的工作方式。

因此,请考虑以下代码。内部函数是一个python闭包,并且var不是局部的outer-它存储在单元格中(然后从单元格中检索):

def outer():
    var = 1

    def inner():
        return var

    return inner

反汇编如下所示:

0  LOAD_CONST               1 (1)
3  STORE_DEREF              0 (var)  # not STORE_FAST

6  LOAD_CLOSURE             0 (var)
9  BUILD_TUPLE              1
12 LOAD_CONST               2 (<code object inner at 0x10796c810)
15 LOAD_CONST               3 ('outer.<locals>.inner')
18 MAKE_CLOSURE             0
21 STORE_FAST               0 (inner)

24 LOAD_FAST                0 (inner)
27 RETURN_VALUE

recursing into <code object inner at 0x10796c810:

0  LOAD_DEREF               0 (var)  # same thing
3  RETURN_VALUE

当我们尝试将其他东西绑定到var内部函数内部时,这种情况会改变:

def outer():
    var = 1

    def inner():
        var = 2
        return var

    return inner

再一次反汇编:

0  LOAD_CONST               1 (1)
3  STORE_FAST               0 (var)  # this one changed
6  LOAD_CONST               2 (<code object inner at 0x1084a1810)
9  LOAD_CONST               3 ('outer.<locals>.inner')
12 MAKE_FUNCTION            0  # AND not MAKE_CLOSURE
15 STORE_FAST               1 (inner)

18 LOAD_FAST                1 (inner)
21 RETURN_VALUE

recursing into <code object inner at 0x1084a1810:

0  LOAD_CONST               1 (2)
3  STORE_FAST               0 (var)  # 'var' is supposed to be local

6  LOAD_FAST                0 (var)  
9  RETURN_VALUE

我们var在本地存储,这与文档中所说的相符: 名称的分配总是进入最内部的作用域

现在,当我们尝试增加时var += 1,会出现一个讨厌的LOAD_FAST东西,它试图varinner的本地范围获取:

14 LOAD_FAST                0 (var)
17 LOAD_CONST               2 (2)
20 INPLACE_ADD
21 STORE_FAST               0 (var)

当然,我们会得到一个错误。现在, 这是我没有得到的
:为什么我们不能使用来检索,然后var用来LOAD_DEREF将其存储在inner范围内STORE_FAST?我的意思是,这对于“内部作用域”赋值似乎是可以的,同时在某种程度上更直观。至少+=代码可以完成我们想要的工作,而我无法提出一种情况,其中所描述的方法可能会使事情变得混乱。

你能?我觉得我在这里错过了一些东西。


问题答案:

Python有一条非常简单的规则,即将作用域中的每个名称分配给一个类别:本地,封闭式或全局/内置。

(当然,CPython通过使用FAST局部变量,DEREF闭合单元以及NAME或GLOBAL查找来实现该规则。)

更改后的规则对于简单的情况确实很有意义,但是很容易提出模棱两可的情况(至少对于人类读者而言,如果不是编译器)。例如:

def outer():
    var = 1

    def inner():
        if spam:
            var = 1
        var += 1
        return var

    return inner

这样var += 1LOAD_DEREFLOAD_FAST吗?直到我们知道spam运行时的值,我们才能知道。这意味着我们不能编译函数体。

即使您可以提出一个更有意义的复杂规则,但该规则的内在优点也很简单。除了易于实现(因此易于调试,优化等)之外,其他人也很容易理解。当您获得时UnboundLocalError,任何中级Pythonhtml" target="_blank">程序员都知道如何在脑海中遍历该规则并找出出了什么问题。

同时,请注意,当在现实生活中的代码中出现这种情况时,有很简单的方法可以明确地解决它。例如:

def inner():
    lvar = var + 1
    return lvar

您想加载闭包变量,并分配给局部变量。他们没有理由需要使用相同的名称。实际上,即使使用新规则,使用相同的名称也会产生误导,这实际上向读者暗示您正在修改闭包变量,而实际上并非如此。因此,只要给他们起不同的名字,问题就解决了。

这仍然适用于非本地分配:

def inner():
    nonlocal var
    if spam:
        var = 1
    lvar = var + 1
    return lvar

或者,当然,还有一些技巧,例如使用参数默认值来创建以闭包变量的副本开头的本地:

def inner(var=var):
    var += 1
    return var


 类似资料:
  • 问题内容: 我有一个下拉列表。我想添加图像。我尝试在tha 标签中添加标签…但是仍然无法显示图像。如何使用标签? 问题答案: 这是不可能的,因为仅支持text。 您可能必须使用复杂的HTML / CSS / JavaScript来滚动自己的下拉控件。如何做可能不在您的问题范围内。 或者,您可以使用非重复,并在文本上加上一些填充以达到类似的效果。但是,如果每个人都有一个唯一的图像,那么您的代码将被每

  • 自从ggplot2.2.2更新之后,这个主题似乎就没有涵盖,像这样的旧解决方案和这个解决方案不再适用。幸运的是,这个过程比以前简单得多。一行代码,你有一个次要的Y轴(如图所示)。 但是我不能在我的地块上得到第二个X轴... 我正在比较沉积物核心沿线金属浓度的深度剖面。我想将碳和磷酸盐浓度显示为金属浓度后面的一个几何区域。问题是碳和磷酸盐的浓度与金属不在同一范围内。因此,我需要第二个轴。 主题如下(

  • 问题内容: 我想在包含引号前缀的xlsx工作簿工作表中添加一个单元格,而我正在尝试使用POI库创建该工作表。如何添加这种类型的单元格 我在Maven Central上使用CTXf.setQuotePrefix(boolean quotePrefix)找到了对此的引用,但不知道如何将其添加到XSSFCell 我尝试使用下面的代码 得到例外 谁能帮我这个 问题答案: 的,也是财产的一部分,而不是。 因

  • 使用以下数据帧: 我想获得 我分别尝试了和 但我不知道如何将两者结合起来,或者是否有其他方法。

  • 我已经用FXML定义了一个tableview。它类似于以下内容: Action列将在每一行中包含带有文本“Delete”的按钮。我有两个问题: 如何将此删除按钮添加到JavaFX中的每一行最后一个单元格? 如何获取已单击“删除”按钮的行的索引?(以便删除该行或进行其他事件处理工作)

  •        其使用原理同6.1.1

  • 添加地标        点击菜单栏或底下快捷工具栏中的“添加地标”按钮,按照弹出的对话框,在当前地图的中心添加一个地标来标注位置。        绘制完成后弹出属性对话框,可在“说明”栏里查看并修改对于该地标的说明。        可在“空间信息”栏里填写地标的定位信息,有小数格式、度分秒格式、度分格式三种输入方式。        可在“样式”栏里修改图标风格(图标样式、颜色、大小、透明度等)和标

  • 添加地标        其使用原理同6.1.1