【问题标题】:Exceptions where the exception handler is in a separate module异常处理程序位于单独模块中的异常
【发布时间】:2012-04-29 14:43:54
【问题描述】:

考虑以下两个模块,prog.py 和 err.py。这两个都是一个更大的程序的例子,它表现出这个问题。我需要将功能分成更小的源文件,每个文件都有一个测试台。

Err.py 包含一个测试台。当它创建一个 prog 对象时,取决于异常被调用的方式,异常是否被捕获。

似乎即使 prog.py 在“from err import *”语句中导入了 err 对象,模块名称仍然被推断(错误?),但似乎它所指的 err 与模块不同自己

这是 Python 2.7 中的错误还是预期行为?

只需获取两个文件并运行 err.py 即可了解我的意思..

第一个文件:

#prog.py

from err import *



class prog(object):
    def func1(self):
        raise MySubError

    def func2(self):
        doError()

还有第二个文件:

#err.py
import prog
import inspect
import sys

class myError(Exception):
    pass

class MySubError(myError):
    pass

def doError():
    raise MySubError

if __name__=="__main__":

    p=prog.prog()


    try:
        doError()
    except MySubError as er:
        print type(er)
        print "Captured 1"
    except Exception as er:
        print type(er)
        print "Not Captured 1"        

    try:
        p.func1()
    except MySubError as er:
        print type(er)
        print "Captured 2"
    except Exception as er:
        print type(er)
        print "Not captured 2"


    try:
        p.func2()
    except MySubError as er:
        print type(er)
        print "Captured 3"
    except Exception as er:
        print type(er)
        print "Not captured 3"

在我看来,好像 err 应该知道它是什么模块,并且异常应该是 err.MySubError,而不仅仅是 MySubError。我可以获取模块,但不能获取实例.....

输出:

<class '__main__.MySubError'>
Captured 1
<class 'test.MySubError'>
Not captured 2
<class 'test.MySubError'>
Not captured 3

【问题讨论】:

    标签: python exception module


    【解决方案1】:

    这里的问题是err 实际上充当了两个不同的模块,本质上。

    在 Python 中运行的主模块称为__main__。执行的所有内容都放入它的命名空间。这里发生的事情是您将模块导入到您的其他脚本中,它被称为err,因此它们不被视为相同。

    这有点奇怪,但有必要保持 Python 导入系统的工作方式一致。这里最好的答案是将您的异常移到您的脚本之外,称为__main__。无论如何,这是一个很好的设计,因为您实际上是在此处创建循环导入。

    【讨论】:

    • 所以我必须有 err.py 和 testerr.py 而不是 err.py?问题是所有模块都必须有一个测试夹具。我想我可以使用 exec() 来调用 err 的测试工具,但这有点 hacky。
    • 好的,我不喜欢它,因为它对我来说似乎是一种矛盾。作为 MySubError 引发的异常应该保持它的类型,而不是仅仅因为它的导入方式而被重命名。但是为了让我重新开始,我很高兴将模块 test 移到外部,因为我发现在 err 中导入 testerr 并调用它的测试函数在所有 synarios 中都有效。
    • 绝对不要使用exec(),那会让你的别名问题变得更糟。只需避免将脚本导入到自身中,它就会按预期工作。 Python 非常擅长跨导入跟踪命名空间。
    【解决方案2】:

    问题是 python 不知道err.py 与您的主脚本相同。通常一个模块只导入一次,但是当主脚本将自己作为一个模块导入时,python 会感到困惑,你实际上是在加载 err.py 两次。 (我不知道有什么好的理由;也许@Latty 可以更具体)。

    您可以通过这个简单的脚本看到问题:

    # File: recur.py
    import recur
    print "This is the module"
    
    if __name__ == '__main__':
        print "This is main"
    

    如果你运行它,你将不会获得无限递归,因为模块只被导入一次。但是您会看到两次“这是模块”。

    只要您的主脚本不导入自身(直接或间接),将异常从一个模块导入另一个模块就没有问题。让两个相互依赖的模块 A 和 B 相互导入也没有问题:试试import A,您会发现它们每个都只加载一次。

    编辑:虽然我真的认为您应该避免将主脚本作为模块导入,但我只是为您的示例想到了一个修复:

    # err.py
    ...
    if __name__=="__main__":
        from prog.err import *     # add this line
    

    这会将所有重复的脚本类替换为其模块版本,并且一切都会按预期工作。

    【讨论】:

    • 是的,我知道这一点,这是有道理的。没有意义的是正在更改的异常的类命名。我仍然看不到它的原因。毕竟,最终它是同一个类实例。我没有使用 exec,我只是使用了一个模块函数 testerr.TestError() 并从两个模块中调用它,这样它们都可以与全局测试模块一起使用。
    • Jason:但考虑到您构建代码的方式,它不是同一个类实例。如果我错了,也许 Latty 和 alexis 可以纠正我,但我相信:既然你在 prog.py 内做 from err import *,这会在 err.pyprog.py 内创建新的本地副本。这就是为什么在 prog.py 中异常类被命名为 err.MySubError,而在 err.py 中它被命名为 __main__.MySubError。我相信您实际上提出了两个不同的例外,具体取决于 raise 的位置。
    • @alan,你错了。 :-) 将异常从一个常规模块导入另一个按预期工作,我过去曾尝试过。问题是python无法识别脚本与模块相同。我能想到的唯一借口:脚本名称不需要是有效的模块名称,并且它们在模块导入路径中没有位置,因此 python 没有很好的方法来跟踪它们。
    • @Jason,python 无法将模块 err 和您的主脚本识别为“同一个类实例”。这是问题的唯一程度。如果您在prog.py 中定义错误并将它们导入到您的主脚本中(并且不要在任何地方导入err.py),一切都会正常工作。
    • @alan, you are wrong. :-) 好吧,这不会是最后一次 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    • 2022-08-13
    • 2012-03-04
    相关资源
    最近更新 更多