查找所有内置异常的简单函数在直接运行时有效,但在用作导入模块时失败,不提供回溯

问题描述 投票:1回答:2

我有一个函数应该生成所有内置异常的元组(用于except (Exception1, Exception2, etc...) as error:形式),当我正常运行时,它工作得很好。

def get_exceptions():

    exceptionList = []

    for item in dir(__builtins__):
        if item.find('Error') != -1:
            exec('exceptionList.append({})'.format(item))

    return tuple(exceptionList)

if __name__ == '__main__':
    print(get_exceptions())

并在运行时:

(<class 'ArithmeticError'>, <class 'AssertionError'>, <class 'AttributeError'>, <class 'BlockingIOError'>, <class 'BrokenPipeError'>, <class 'BufferError'>, <class 'ChildProcessError'>, <class 'ConnectionAbortedError'>, <class 'ConnectionError'>, <class 'ConnectionRefusedError'>, <class 'ConnectionResetError'>, <class 'EOFError'>, <class 'OSError'>, <class 'FileExistsError'>, <class 'FileNotFoundError'>, <class 'FloatingPointError'>, <class 'OSError'>, <class 'ImportError'>, <class 'IndentationError'>, <class 'IndexError'>, <class 'InterruptedError'>, <class 'IsADirectoryError'>, <class 'KeyError'>, <class 'LookupError'>, <class 'MemoryError'>, <class 'NameError'>, <class 'NotADirectoryError'>, <class 'NotImplementedError'>, <class 'OSError'>, <class 'OverflowError'>, <class 'PermissionError'>, <class 'ProcessLookupError'>, <class 'ReferenceError'>, <class 'RuntimeError'>, <class 'SyntaxError'>, <class 'SystemError'>, <class 'TabError'>, <class 'TimeoutError'>, <class 'TypeError'>, <class 'UnboundLocalError'>, <class 'UnicodeDecodeError'>, <class 'UnicodeEncodeError'>, <class 'UnicodeError'>, <class 'UnicodeTranslateError'>, <class 'ValueError'>, <class 'OSError'>, <class 'ZeroDivisionError'>)

这就是我想要的。但是,在下面,通过shell,

>>> import list_exceptions
>>> list_exceptions.get_exceptions()
()

什么都没发生。

即使在文件中:

import list_exceptions
print(list_exceptions.get_exceptions())

我得到:

()

这看起来很奇怪。任何帮助都会很棒!顺便说一下,我看着这些,它们与我的想法并没有太大关系。 import fails when running python as script, but not in iPython? http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html

如果你有问题,就问吧 :)

python python-3.x exception
2个回答
2
投票

你的方法的根本问题在于你依赖于两件你不应该依赖的东西。第一个是dir,其行为不应该依赖,因为它主要用于帮助交互式shell中的调试。来自docs

如果对象不提供__dir__(),则该函数会尽力从对象的__dict__属性(如果已定义)和其类型对象中收集信息。结果列表不一定完整,并且当对象具有自定义__getattr__()时可能不准确。

...

注意因为dir()主要是为了方便在交互式提示中使用而提供的,所以它试图提供一组有趣的名称,而不是尝试提供严格或一致定义的名称集,并且其详细行为可能会在不同版本之间发生变化。例如,当参数是类时,元类属性不在结果列表中。

此外,您使用__builtins__变量,这是一个实现细节,再次来自docs

作为一个实现细节,大多数模块都将名称__builtins__作为其全局变量的一部分提供。 __builtins__的值通常是此模块或此模块的__dict__属性的值。由于这是一个实现细节,因此Python的替代实现可能不会使用它。

从本质上讲,你依赖于两个不可靠的东西。请注意,在您的情况下实际发生的是,当您直接运行模块时,它会返回实际的builtins模块,但是,在导入模块时,__builtins__包含“此模块的值为__dict__”。一些调试打印可能会照亮这个:

# builtinstest.py
def get_exceptions():

    print(type(__builtins__))
    print(dir(__builtins__))

从交互式翻译:

>>> import builtinstest
>>> builtinstest.get_exceptions()
<class 'dict'>
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

因此,当您在dir对象上调用dict时,它只返回从dict-object内省的属性,例如: copyfromkeysgetitems和所有其他dict方法。解决方案是使用builtins模块并且不使用dir,使用vars(它只返回__dict__属性),因为您需要模块对象的属性。

最后,你使用exec的方法并不好。如果你想要这样做,请检查它是否是BaseException的子类,它是所有内置异常的父类,所以来自the docs

例外BaseException

所有内置异常的基类。它并不意味着由用户定义的类直接继承(为此,使用Exception)。

所以类似于:

import builtins
def get_exceptions_sanely():
    exception_list = []
    for obj in  vars(builtins).values():
        if isinstance(obj, type) and issubclass(obj, BaseException):
            exception_list.append(obj)
    return tuple(exception_list)

你正在努力实现的目标。请注意,这会直接迭代值,因此您最终不会使用像evalexec这样的东西,在这种情况下这是滥用。请注意,这会捕获每个内置异常,例如警告(例如BytesWarning)和更多深奥的东西,如SystemExit

Finally

仅仅因为你可以这样做,并不意味着你应该这样做。您声明的目的是:

我有一个函数应该生成所有内置异常的元组,(用于except (Exception1, Exception2, etc...) as error:形式)

好吧,你可以使用except BaseException as error而不是通过首先找到那些例外的琐事(事实上,except <something>基本上检查<something> issubclass是否有任何错误被提出。从根本上说,很少有任何理由有这样的错误。一个广泛的except条款。你应该尽可能地抓住一个尽可能狭窄的例外。


1
投票

这是有效的,似乎是获取内置模块的常用技术。

def get_exceptions():

    exceptionList = []

    for k in __builtins__.keys():
        if k.find('Error') != -1:
            exec('exceptionList.append({})'.format(k))

    return tuple(exceptionList)


if __name__ == '__main__':
    print(get_exceptions())
© www.soinside.com 2019 - 2024. All rights reserved.