python在线编译原理_关于编译:Python局部变量编译原理

def fun():

if False:

x=3

print(locals())

print(x)

fun()

输出和错误消息:

{}

---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

in ()

4     print(locals())

5     print(x)

----> 6 fun()

in fun()

3         x=3

4     print(locals())

----> 5     print(x)

6 fun()

UnboundLocalError: local variable 'x' referenced before assignment

我想知道python解释器是如何工作的。 请注意,x = 3根本不运行,并且不应将其视为局部变量,这意味着错误将是"名称'x'未定义"。 但是查看代码和错误消息,情况并非如此。 谁能解释一下这种情况背后的python解释器编译的机制原理?

可能重复stackoverflow.com/q/7969949/3758972

这可能是相关的:范围规则的简短描述

如果下面的答案之一解决了您的问题,您应该接受它(单击相应答案旁边的复选标记)。 这样做有两件事。 它让每个人都知道您的问题已得到解决,让您满意,并且它可以帮助您获得帮助。 请参阅此处以获取完整说明。

因此,Python将始终将每个函数中的每个名称分类为本地名称,非本地名称或全局名称。这些名称范围是独家的;在每个函数中(嵌套函数中的名称都有自己的命名范围),每个名称只能属于这些类别中的一个。

当Python编译这段代码时:

def fun():

if False:

x=3

它将产生一个抽象语法树,如:

FunctionDef(

name='fun',

args=arguments(...), b

body=[

If(test=NameConstant(value=False),

body=[

Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=3))

],

orelse=[])

]

)

(为简洁起见省略了一些东西)。现在,当这个抽象语法树被编译成代码时,Python将扫描所有名称节点。如果有Name的任何Name节点,则该名称被认为是封闭的FunctionDef的本地名称(如果有),除非用global(即global x)或nonlocal(nonlocal x)覆盖)同一函数定义中的语句。

ctx=Store()主要发生在有问题的名称在赋值的左侧使用,或者作为for循环中的迭代变量时。

现在,当Python将其编译为字节码时,生成的字节码就是

>>> dis.dis(fun)

4           0 LOAD_GLOBAL              0 (print)

3 LOAD_FAST                0 (x)

6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)

9 POP_TOP

10 LOAD_CONST               0 (None)

13 RETURN_VALUE

优化器完全删除了if语句;但是由于变量已经标记为函数的局部变量,LOAD_FAST用于x,这将导致从局部变量和局部变量访问x。由于尚未设置x,因此抛出UnboundLocalError。另一方面,名称print从未被赋值,因此被认为是此函数中的全局名称,因此其值加载LOAD_GLOBAL。

函数中使用的名称只能包含整个函数体的一个范围。范围在编译时确定(而不是在运行函数时)。

如果在函数中的任何位置都有对名称的赋值(无论是否在调用函数时运行它),编译器都会默认将该名称视为函数的本地名称。您可以使用global和nonlocal语句明确告诉它使用不同的范围。

一种特殊情况是在一个函数体中分配名称,并从第一个函数中定义的另一个函数访问。这样的变量将放在一个特殊的closure单元格中,该单元格将在函数之间共享。外部函数将变量视为局部变量,而内部函数只能在其具有名称的nonlocal语句时才分配给它。这是一个闭包和nonlocal语句的示例:

def counter():

val = 0

def helper():

nonlocal val

val += 1

return val

return helper

除了您所看到的问题之外,您可能会看到另一种范围混淆:

x = 1

def foo():

print(x)  # you might expect this to print the global x, but it raises an exception

x = 2     # this assignment makes the compiler treat the name x as local to the function

在foo函数中,名称x在任何地方都被视为本地,即使print调用在将其分配到本地名称空间之前尝试使用它。

谢谢你提到关闭。

x = 3无法访问的事实无关紧要。该函数分配给它,因此它必须是本地名称。

请记住,在执行开始之前编译整个文件,但是在执行阶段定义函数,当执行编译的函数定义块时,创建函数对象。

复杂的优化器可以消除无法访问的代码,但CPython的优化器并不那么聪明 - 它只执行非常简单的锁孔优化。 s>

要深入了解Python内部,请查看ast和dis模块。

你能详细说明一下吗?

The fact that x = 3 is unreachable is irrelevant. The function assigns to it, so it must be a local name.所以你说locals()不会显示x,因为它只是一个已定义的名称而没有分配给值? (或类似的东西)

@vikash因为变量在创建之前不存在。因为永远不会执行永远不会执行的赋值。

所以本地名称和局部变量是两个不同的东西。有没有办法看到本地名称列表,就像我们有局部变量的locals()。

@vikash为了更准确,Python实际上没有变量,它有可能绑定到一个或多个名称的对象。 locals()仅显示当前绑定到本地名称的对象。可能有一种简单的方法来显示所有被认为属于本地范围的名称,但我在手机上,所以我不容易调查。 ;)此外,评论不是讨论的地方。如果您无法在档案中找到相关内容,或者在Python聊天室中询问,也许可以提出一个新问题。

注意:优化器确实删除了无法访问的if False:块,至少在Python 3.5上(它不能在Py 2.x上,因为False不是那里的特殊常量,并且名称可能会反弹,即使这是一个可怕的想法)。但是x仍然是局部的,我假设是因为局部变量的扫描首先发生,并且不可达的块消除不会撤消x的局部性质。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值