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

Posted

技术标签:

【中文标题】异常处理中的这种奇怪行为是啥?【英文标题】:What is this strange behavior in exception handling?异常处理中的这种奇怪行为是什么? 【发布时间】:2017-03-30 00:09:34 【问题描述】:

我正在使用 python 2.7,但尝试编写一个代码来检查对象是否也是与 python 3+ 兼容的 basestring 的子类。我尝试遵循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

但在这种情况下不会打印任何内容。

【问题讨论】:

【参考方案1】:

当您将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)

请注意,对于foobasestring 是使用LOAD_FAST 操作码检索的(这意味着它正在寻找一个局部变量)。然而,在bar 中,basestring 是使用LOAD_GLOBAL 操作码检索的。

【讨论】:

【参考方案2】:

在第一种情况下很简单,名称 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 中的错误。这是运行时的错误,而不是定义时的错误。第三种情况与第一种情况类似——“局部变量”的概念不适用于全局范围。

【讨论】:

"请注意,函数的本地名称是在函数定义时确定的" 这正是我所缺少的!谢谢!

以上是关于异常处理中的这种奇怪行为是啥?的主要内容,如果未能解决你的问题,请参考以下文章

怎么处理Java中的异常

使用 Postmarkapp 在 Rails 应用程序中处理电子邮件异常的最佳方法是啥?

python中的异常处理

编程思想之异常处理:C++JavaJavaScript中的异常处理(Exception)

在 Perl 中处理异常的最佳方法是啥?

如何检测业务数据中的异常