【问题标题】:Python unreachable variable assignment resulting in UnboundLocalErrorPython 无法访问的变量赋值导致 UnboundLocalError
【发布时间】:2016-12-12 04:11:34
【问题描述】:

我试图了解 Python 如何在内部管理变量。

x = 10
def ex1():
    if False:
        x=1
    print(x)
ex1()

执行 ex1() 时,它显示 UnboundLocalError,因为未引用局部变量 'x'。

这是怎么发生的? 解析是否发生在初始通道中,只是创建符号表并指定范围,然后是在另一通道中发生的解释并跳过x=1,因为它无法访问?

【问题讨论】:

    标签: python variables scope interpreter


    【解决方案1】:

    Python 没有变量声明,因此在创建/使用变量时,它必须自行确定范围。 Python 作用域是词法的,这意味着它可以访问其封闭范围内的变量,但不能修改它们。 ex1() 的写法,x=1 是 ex1() 的局部。但是,当您运行 ex1() 时,它会尝试读取 x=10,因为它是本地的,并抛出您的 UnboundLocalError。 因此,管理变量的方式是,它看到一个局部声明,运行函数,然后看到另一个局部声明,并且由于范围,不能将两者联系起来。

    【讨论】:

      【解决方案2】:

      从概念上讲,这是有道理的。我不知道它是如何实现的,但我可以说出原因。

      当您影响一个变量时,它会在本地范围内受到影响,除非您使用关键字global 明确告知。如果你只访问它并且没有做作,它将隐式使用全局变量,因为没有定义局部变量。

      x = 10
      
      def access_global():
          print x
      
      def affect_local():
          x = 0
          print x
      
      def affect_global():
          global x
          x = 1
          print x
      
      access_global() # 10
      affect_local()  # 0
      print x         # 10
      affect_global() # 1
      print x         # 10
      

      如果您在嵌套函数、类或模块中执行此操作,则规则类似:

      def main():
          y = 10
          def access():
              print y
          def affect():
              y = 0
              print y
      
          access() # 10
          affect() # 0
          print y  # 10
      
      main()
      

      这可能会节省数小时的痛苦调试,除非明确说明,否则永远不会覆盖父范围的变量。

      编辑

      反汇编python字节码为我们提供了一些额外的信息来理解:

      import dis
      x = 10
      def local():
          if False:
              x = 1
      
      def global_():
          global x
          x = 1
      
      print local
      dis.dis(local)
      print global_
      dis.dis(global_)
      
      
      <function local at 0x7fa01ec6cde8>
       37           0 LOAD_GLOBAL              0 (False)
                    3 POP_JUMP_IF_FALSE       15
      
       38           6 LOAD_CONST               1 (1)
                    9 STORE_FAST               0 (x)
                   12 JUMP_FORWARD             0 (to 15)
              >>   15 LOAD_CONST               0 (None)
                   18 RETURN_VALUE        
      <function global_ at 0x7fa01ec6ce60>
       42           0 LOAD_CONST               1 (1)
                    3 STORE_GLOBAL             0 (x)
                    6 LOAD_CONST               0 (None)
                    9 RETURN_VALUE      
      

      我们可以看到local函数的字节码调用STORE_FASTglobal_函数调用STORE_GLOBAL

      这个问题还解释了为什么将函数转换为字节码以避免每次调用函数时都进行编译的性能更高: Why python compile the source to bytecode before interpreting?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-14
        • 2018-06-29
        • 1970-01-01
        • 2012-11-14
        • 2013-11-29
        • 2019-09-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多