异常处理中的这种奇怪行为是什么?

时间:2016-11-15 22:56:23

标签: python python-2.7 exception-handling

我正在使用python 2.7,但是试图创建一个代码来检查对象是否是与python 3+兼容的basetring的子类。我试图遵循suggested here方法,并在流程中发现了一个我不理解的行为

如果我这样做:

def foo():
    try: basestring
    except NameError:
        print "a"
foo()
没有任何反应。

如果我在except:

里面略微修改了那段代码
def foo():
    try: basestring
    except NameError:
        print "a"
        basestring=str
foo()

然后打印“a”。

我不明白如何向except块添加内容,可能会影响异常的触发。

我在函数外检查了相同的代码:

try:
    basestring
except NameError:
    print("a")
    basestring=str

但在这种情况下没有打印出来。

2 个答案:

答案 0 :(得分:5)

当你向函数添加basestring = str时,你告诉python应该将basestring视为局部变量。但是,在执行第一个语句时, 没有名为basestring的局部变量(只有全局),因此python引发UnboundLocalError

由于UnboundLocalError继承自NameError,您的异常处理会被触发,您会看到a已打印出来。

如果你对这个细节很感兴趣 - 我们可以使用dis将其分开:

import dis

def foo():
    try:
        basestring
    except NameError:
        print("a")
        basestring=str

def bar():
    try:
        basestring
    except NameError:
        print("a")

dis.dis(foo)
print('--' * 20)
dis.dis(bar)

请注意,对于foo,使用basestring操作码检索LOAD_FAST(这意味着它正在寻找本地变量)。但是,在bar中,使用basestring操作码检索LOAD_GLOBAL

答案 1 :(得分:3)

在第一种情况下,很容易,basestring上已解析名称__builtins__.basestring。 try块没有引发异常,因此行为应该符合预期。

在第二种情况下,这很棘手。在函数内使用名称basestring使该名称成为函数的局部变量。 请注意,函数本地的名称是在函数定义时确定的。执行函数的第一行时,Python已经知道名称basestring是函数的局部变量。

>>> def foo():
...     basestring
...     potato
...     errorerrorerror
...     
>>> print foo.func_code.co_names
('basestring', 'potato', 'errorerrorerror')
>>> print foo.func_code.co_varnames
()

foo()行调用NameError potato。与下面的bar()进行比较和对比,NameError就行了basestring

>>> def bar():
...     basestring
...     potato
...     errorerrorerror
...     basestring = "D'Addario EXL160 Medium"
...     
>>> print bar.func_code.co_names
('potato', 'errorerrorerror')
>>> print bar.func_code.co_varnames
('basestring',)

因此,引发的异常是由于在绑定到对象之前使用了名称,这在Python中是一个错误。这是运行时的错误,而不是定义时的错误。第三种情况与第一种情况类似 - “局部变量”的概念不适用于全球范围。