为什么python自省不知道函数中的异常? 当我处理一些代码异常代码时,我想知道为什么Python没有在它的内省系统中包含函数会增加什么异常。例如,当我必须使用一个函数来引用许多其他的函数,这些函数会引起不同的异常时,我不得不考虑在我的业务逻辑中发生的所有事情。 像这样: def a(): raise Exception('exception1') def b(): a() raise Exception('exception2') def c(): b() raise Exception('exception3') def business(): try: c() except Exception as e: pass

为什么python自省不知道函数中的异常? 当我处理一些代码异常代码时,我想知道为什么Python没有在它的内省系统中包含函数会增加什么异常。例如,当我必须使用一个函数来引用许多其他的函数,这些函数会引起不同的异常时,我不得不考虑在我的业务逻辑中发生的所有事情。 像这样: def a(): raise Exception('exception1') def b(): a() raise Exception('exception2') def c(): b() raise Exception('exception3') def business(): try: c() except Exception as e: pass,python,exception,Python,Exception,我必须继续挖掘它们之间的函数调用,这样我才能知道这个代码块中可能会出现什么。而内省系统没有异常信息 正如我所知,Java将在函数定义中显式地注释“Throw”,IDE和程序员可以很容易地知道我应该处理什么样的异常 如果我能知道对象本身的所有异常,那就更好了,例如: all_exception = obj.__exceptions__() 所以,我的问题是,为什么python不在函数对象中包含异常内省。 谁能解释python的设计呢?python是一种动态语言,您无法预先知道函数会抛出哪些异常

我必须继续挖掘它们之间的函数调用,这样我才能知道这个代码块中可能会出现什么。而内省系统没有异常信息

正如我所知,Java将在函数定义中显式地注释“Throw”,IDE和程序员可以很容易地知道我应该处理什么样的异常

如果我能知道对象本身的所有异常,那就更好了,例如:

all_exception = obj.__exceptions__()
所以,我的问题是,为什么python不在函数对象中包含异常内省。
谁能解释python的设计呢?

python是一种动态语言,您无法预先知道函数会抛出哪些异常

举个例子:

def throw(exception):
    raise exception
该函数将引发什么异常?我可以使用throwValueError或throwTypeError'foobar',两者都可以工作并且都是有效的Python:

>>> throw(ValueError)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in throw
ValueError
>>> throw(TypeError('foobar'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in throw
TypeError: foobar
这就是为什么Python函数不能公开它们引发的异常

请注意,使用普通异常从来没有好的理由。在ValueError、TypeError、IndexError、KeyError等有意义的情况下使用标准异常之一,或者通过从Exception或更具体的Exception子类进行子类化来创建自己的API特定异常

然后正确地记录您的API。在需要的地方说明开发人员应该期望的异常。标准例外情况不需要详细说明;很明显,如果您传入一个file对象,则只对字符串起作用的函数将抛出TypeError

如果需要捕获多种类型,可以在业务应用程序中使用异常类层次结构:

class BusinessException(Exception):
    """The base exception for all of Business APIs"""

class SpecificBusinessException(BusinessException):
    """Exception that indicates a specific problem occurred"""

class DifferenBusinessException(BusinessException):
    """Exception that indicates a different specific problem occurred"""
然后引发子类异常并捕获BusinessException以处理所有异常,或者仅捕获特定子类以自定义处理

如果您必须找出代码引发的异常,并接受动态语言能够更改名称所涉及的风险,那么您至少可以使用来查找有关异常的一些信息。对于直接提升名称和提升名称参数。。语句,通过遍历AST提取这些名称或调用至少相对简单:

import builtins
import inspect
import ast

class ExceptionExtractor(ast.NodeVisitor):
    def __init__(self):
        self.exceptions = []
    def visit_Raise(self, node):
        if node.exc is None:
            # plain re-raise
            return
        exc_name = node.exc
        if isinstance(exc_name, ast.Call):
            exc_name = exc_name.func
        if not (isinstance(exc_name, ast.Name) and
                isinstance(exc_name.ctx, ast.Load)):
            # not a raise Name or raise Name(...)
            return
        self.exceptions.append(exc_name.id)

def global_exceptions_raised(func):
    """Extract the expressions used in raise statements

    Only supports raise Name and raise Name(...) forms, and
    only if the source can be accessed. No checks are made for the
    scope of the name.

    returns references to those exception names that can be loaded from
    the function globals.

    """
    source = inspect.getsource(func)
    tree = ast.parse(source)
    extractor = ExceptionExtractor()
    extractor.visit(tree)
    fglobals = {**func.__globals__, **vars(builtins)}
    return [fglobals[name] for name in extractor.exceptions if name in fglobals]

可能相关:Guido van Rossum,可能?你没有关于异常的信息是什么意思?您已将异常存储在变量e中,可以对其执行任何操作。要理解python设计,只需在python shell中输入import this。并在每次询问python时重复该命令。当你这样做的时候,它应该会让你明白为什么你不想用Python或任何其他语言编写这样的异常。a在一般意义上,Python无法知道哪些导入的例程引发了哪些异常,因为它没有声明,例如在Java中。b您的代码引发的通用异常实例将不会赢得任何价格。
import builtins
import inspect
import ast

class ExceptionExtractor(ast.NodeVisitor):
    def __init__(self):
        self.exceptions = []
    def visit_Raise(self, node):
        if node.exc is None:
            # plain re-raise
            return
        exc_name = node.exc
        if isinstance(exc_name, ast.Call):
            exc_name = exc_name.func
        if not (isinstance(exc_name, ast.Name) and
                isinstance(exc_name.ctx, ast.Load)):
            # not a raise Name or raise Name(...)
            return
        self.exceptions.append(exc_name.id)

def global_exceptions_raised(func):
    """Extract the expressions used in raise statements

    Only supports raise Name and raise Name(...) forms, and
    only if the source can be accessed. No checks are made for the
    scope of the name.

    returns references to those exception names that can be loaded from
    the function globals.

    """
    source = inspect.getsource(func)
    tree = ast.parse(source)
    extractor = ExceptionExtractor()
    extractor.visit(tree)
    fglobals = {**func.__globals__, **vars(builtins)}
    return [fglobals[name] for name in extractor.exceptions if name in fglobals]