Python 3.4中的UnboundLocalError

时间:2015-01-23 21:06:46

标签: python

以下代码适用于Python 2.7,但在Python 3.4中引发了这个异常:

  File "/home/sean/dev/ving/meridian/venv/src/django-testmigrate/django_testmigrate/base.py", line 70, in __getattr__
    if e:
UnboundLocalError: local variable 'e' referenced before assignment

e虽然分配在同一功能的顶部。我假设Python 3中有一些新的作用域规则,但我找不到任何对它们的引用。

以下是代码:

def __getattr__(self, name):
    e = None

    if not self._store:
        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))

    for state, scope in reversed(list(self._store.items())):
        try:
            val = getattr(scope, name)
        except AttributeError as e:
            continue
        else:
            e = None

            # get fresh instance
            if state != self._current_state and isinstance(val, models.Model):
                model_meta = val.__class__._meta
                model_class = self._current_state.get_model(model_meta.app_label, model_meta.model_name)
                val = model_class.objects.get(pk=val.pk)

            # add this value to the current scope
            setattr(self, name, val)
            break

    if e: # error raised here
        raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name))

    return val

更新

我通过修改我的代码来实现它,如下所示:

except AttributeError as ex:
    # needed for Python 3 compatibility
    e = ex
    continue

不知何故except...as实际上是从本地范围中删除变量e。好像它可能是Python中的一个错误。

1 个答案:

答案 0 :(得分:5)

看来这是changed behavior in python3.x。具体做法是:

  

使用as target分配异常时,会在except子句的末尾清除异常。

如果你进一步阅读,概述了这种变化的合理性(基本上它会阻止当前堆栈框架中的参考循环,导致对象比通常更长寿命)。

还描述了解决方法:

  

这意味着必须将异常分配给其他名称才能在except子句之后引用它。

这基本上就是你已经发现的。


请注意,如果我们查看反汇编源的操作码,我们就可以看到这一点。这是一个简单的程序来演示:

def foo():
  e = None
  for _ in 'foobar':
    try:
      raise AttributeError
    except AttributeError as e:
      pass
    else:
      e = None
  if e:
    raise AttributeError

import dis
dis.dis(foo)

foo()

如果你在python2.x和python3.x上运行它,你会发现一些差异。忽略3.x引发UnboundLocalError,只查看反汇编的源(对于py3.x运行),你可以看到:

...
  7          50 POP_BLOCK
             51 POP_EXCEPT
             52 LOAD_CONST               0 (None)
        >>   55 LOAD_CONST               0 (None)
             58 STORE_FAST               0 (e)
             61 DELETE_FAST              0 (e)
             64 END_FINALLY
             65 JUMP_ABSOLUTE           13
        >>   68 END_FINALLY
...

特别注意DELETE_FAST操作码。如果使用python2.x运行,则不存在。