异常被执行两次并被不同的except块捕获

6 投票
1 回答
1829 浏览
提问于 2025-04-18 18:50

我有以下代码:

file1.py

from file2 import tfun

class TestException(Exception):
    pass

try:
    print 'I am running'
    tfun()
except TestException as e:
    print 'I am caught'
    print type(e)
except Exception as e:
    print 'I am generally caught'
    print type(e)

file2.py

def tfun():
    from file1 import TestException
    raise TestException()

运行 python file1.py 后的输出是:

I am running
I am running
I am caught
<class 'file1.TestException'>
I am generally caught
<class 'file1.TestException'>

首先,为什么这段代码会执行两次呢?我明白导入是递归的,但为什么脚本的代码会再次执行呢?

其次,第二次执行时,虽然错误类型和第一次一样,但却没有被同一个 except 块捕获,这一点我也找不到解释。

最后,我想找个解决办法来处理这个问题,但不想把任何东西移动到新文件里,但似乎没有什么办法。有没有可能解决这个问题呢?

编辑

关于第二个问题,我意识到这是因为代码是在模块级别的。

1 个回答

7

当你把一个模块当作脚本运行时(也就是直接在解释器中输入它的名字,而不是导入它),这个模块会以 __main__ 的名字被加载。

如果你之后在你的程序中导入同一个模块,它会以它真实的名字重新加载和执行。如果你不小心,可能会导致某些事情被执行两次。

http://effbot.org/zone/import-confusion.htm

你实际上是把 file1.py 加载了两次,作为两个不同的模块。第一次加载是因为你在命令行中执行了:

python file1.py

在这种情况下,file1.py 被作为主模块 __main__ 加载。

第二次加载是因为你在代码中使用了导入语句:

from file1 import TestException

在这种情况下,file1.py 被作为模块 file1 加载。

因为 file1.py 被加载为两个不同的模块,所以里面的所有内容都有两个不同的副本。特别是 __main__.TestExceptionfile1.TestException 是不同的。

所以,在 __main__ 中,这一行:

except TestException as e:

捕获的是 __main__.TestException,而 tfun() 却抛出了 file1.TestException。在 file1 中,同样的这一行捕获的是 file1.TestException

在前一种情况下,类型不匹配,所以 except: 语句不会被执行;而在后一种情况下,类型匹配,所以 except: 语句会被执行。

也许这个程序可以更清楚地说明发生了什么:

from file2 import tfun

class TestException(Exception):
    pass

try:
    print 'I am calling file2.tfun from', __name__
    tfun()
    print 'I have called file2.tfun from', __name__
except TestException as e:
    print 'I am caught in %s'%(__name__)
    print type(e), TestException
except Exception as e:
    print 'I am generally caught in %s'%__name__
    print type(e), TestException
print 'I am exiting from',__name__

结果:

$ python file1.py 
I am calling file2.tfun from __main__
I am calling file2.tfun from file1
I am caught in file1
<class 'file1.TestException'> <class 'file1.TestException'>
I am exiting from file1
I am generally caught in __main__
<class 'file1.TestException'> <class '__main__.TestException'>
I am exiting from __main__

一个简单的解决办法是修改 file2.py:

def tfun():
    from __main__ import TestException
    raise TestException()

结果:

$ python file1.py 
I am calling file2.tfun from __main__
I am caught in __main__
<class '__main__.TestException'> <class '__main__.TestException'>
I am exiting from __main__

撰写回答