异常处理中的这种奇怪行为是啥?
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)
请注意,对于foo
,basestring
是使用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 中的错误。这是运行时的错误,而不是定义时的错误。第三种情况与第一种情况类似——“局部变量”的概念不适用于全局范围。
【讨论】:
"请注意,函数的本地名称是在函数定义时确定的" 这正是我所缺少的!谢谢!以上是关于异常处理中的这种奇怪行为是啥?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Postmarkapp 在 Rails 应用程序中处理电子邮件异常的最佳方法是啥?