【问题标题】:Understanding how Python "Compiles" or "Interprets" Function Objects了解 Python 如何“编译”或“解释”函数对象
【发布时间】:2014-03-25 14:15:25
【问题描述】:

我已经阅读了以下帖子,但我仍然不确定。

  1. Python Compilation/Interpretation Process

  2. Why python compile the source to bytecode before interpreting?

如果我有一个包含以下代码的 Python 文件 myfunctions.py。

x  = 3
def f():
    print x
    x = 2

然后,说$ python myfunctions.py 运行得很好。 但是现在对上面的文件做一点小改动。新文件如下所示。

x  = 3
def f():
    print x
    x = 2
f() # there is a function call now

这一次,代码给出了一个错误。现在,我试图理解这种行为。到目前为止,这些是我的结论。

  • Python 为x=3 创建字节码
  • 它创建了一个函数对象 f,快速扫描并具有字节码,该字节码涉及 f 范围内的局部变量,但请注意,Python 中所有语句的字节码不太可能已经构建。
  • 现在,Python 遇到一个函数调用,它知道这个函数调用是合法的,因为存在讨论函数对象 f 及其局部变量的最小字节码。
  • 现在解释器负责执行字节码,但从最初的足迹,它知道 x 在这里是一个局部变量,并说 - “为什么在分配之前要打印?”

有人可以对此发表评论吗?提前致谢。如果之前已经解决过这个问题,我们深表歉意。

【问题讨论】:

  • 我假设额外的缩进不在您的实际文件中,并且例外是 UnboundLocalError?
  • 错误是什么?在第二个例子中,缩进是否​​应该在“def”行被打破?
  • 我得到的错误(修复缩进后)是UnboundLocalError: local variable 'x' referenced before assignment
  • 对这个愚蠢的错误深表歉意。我已经修复了缩进。那不是问题。问题涉及范围。该错误是关于 Python 抱怨在分配变量之前使用了该变量。

标签: python function interpreter


【解决方案1】:

当解释器读取一个函数时,对于它遇到的每个“名称”(变量),解释器决定该名称是本地还是非本地的。使用的标准非常简单......在正文中的任何位置是否有对该名称的赋值语句(除非global 语句)?例如:

def foo():
    x = 3  # interpreter will tag `x` as a local variable since we assign to it here.

如果有对该名称的赋值语句,则该名称被标记为“本地”,否则,它被标记为非本地。

现在,在您的情况下,您尝试打印一个标记为本地的变量,但您在实际到达关键赋值语句之前就这样做了。 Python 寻找一个本地名称,但没有找到它,所以它提出了UnboundLocalError

Python 是非常动态的,它允许你做很多疯狂的事情,这也是它如此强大的部分原因。这样做的缺点是检查这些错误变得非常困难,除非你真的运行这个函数——事实上,python已经决定检查其他任何东西比语法直到函数运行。这就解释了为什么在实际调用函数之前,您永远不会看到异常。


如果您希望 python 将变量标记为全局变量,您可以使用显式的 global1 语句来实现:

x = 3
def foo():
  global x
  print x
  x = 2

foo()  # prints 3
print x  # prints 2

1python3.x 将这个概念更进一步,引入了nonlocal 关键字

【讨论】:

  • 感谢您的回答。但是当 Python 遇到函数定义时,很明显它正在做一些范围检查并跟踪调用函数时将使用的变量。否则,它遇到的第一个 x 将是全局 x(=3),对吗?然后一个本地 x(=2) 会出现吗?但是 Python 清楚地分析了整个函数体,其中哪些变量是局部变量,哪些是全局变量,即使在调用函数之前不会执行语句。
  • @Killer -- 正确。 在读取函数时,而不是在执行时。
【解决方案2】:

mgilson 得到了一半的答案。

另一半是 Python 不会在它不打算执行的函数(或函数对象)中寻找语法错误之外的错误。所以在第一种情况下,由于没有调用f(),所以不会检查操作顺序错误。

在这方面,它不像 C 和 C++ 那样需要预先完全声明所有内容。这有点像 C++ 模板,在实际实例化代码之前,可能不会发现模板代码中的错误。

【讨论】:

  • 谢谢你,@Mike。现在对这些事情更清楚了。
猜你喜欢
  • 1970-01-01
  • 2015-05-12
  • 2011-03-18
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 2010-11-12
  • 1970-01-01
  • 2017-02-26
相关资源
最近更新 更多