异常装饰器是一种通过装饰器(Decorator)机制来捕获和处理函数中异常的技术。当函数中发生异常时,装饰器可以捕获异常并进行处理,也可以记录异常信息或进行其他操作。堆栈跟踪(Stack Trace)是指在发生异常时,系统会输出一个包含异常信息和函数调用链的信息。对于经常使用python做爬虫来说,这些知识点还是要必须要会的。
1、问题背景
在 Python 中,我们经常会使用装饰器来对函数进行包装,以便在函数调用前后执行一些额外的操作。当函数在装饰器中抛出异常时,默认情况下,堆栈跟踪信息将指向装饰器函数,而不是实际引发异常的函数。这使得调试和定位问题变得困难。
2、解决方案
为了保留异常装饰器的堆栈跟踪信息,我们可以使用以下两种方法:
- 使用
raise
语句的三参数形式
在 Python 2.x 中,我们可以使用 raise
语句的三参数形式来指定异常类型、异常实例和堆栈跟踪信息。例如:
class MyError(Exception):
pass
def try_except(fn):
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except Exception as e:
et, ei, tb = sys.exc_info()
raise MyError, MyError(e), tb
return wrapped
def bottom():
1 / 0
@try_except
def middle():
bottom()
def top():
middle()
>>> top()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 24, in top
middle()
File "tmp.py", line 10, in wrapped
return fn(*args, **kwargs)
File "tmp.py", line 21, in middle
bottom()
File "tmp.py", line 17, in bottom
1 / 0
__main__.MyError: integer division or modulo by zero
在上面的例子中,try_except
装饰器会捕获函数 middle
中抛出的异常,并使用 raise
语句的三参数形式重新抛出异常。这样,堆栈跟踪信息就会指向函数 bottom
,而不是函数 middle
。
- 使用
traceback
模块
在 Python 3 中,我们还可以使用 traceback
模块来获取和操作堆栈跟踪信息。例如:
import sys
import traceback
class MyError(Exception):
pass
def try_except(fn):
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs)
except Exception as e:
exc_type, exc_instance, exc_traceback = sys.exc_info()
formatted_traceback = ''.join(traceback.format_tb(
exc_traceback))
message = 'n{0}n{1}:n{2}'.format(
formatted_traceback,
exc_type.__name__,
exc_instance.message
)
raise MyError(message)
return wrapped
def bottom():
1 / 0
@try_except
def middle():
bottom()
def top():
middle()
>>> top()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 24, in top
middle()
File "tmp.py", line 10, in wrapped
return fn(*args, **kwargs)
File "tmp.py", line 21, in middle
bottom()
File "tmp.py", line 17, in bottom
1 / 0
__main__.MyError:
Traceback (most recent call last):
File "tmp.py", line 17, in bottom
1 / 0
ZeroDivisionError: division by zero
在上面的例子中,try_except
装饰器会捕获函数 middle
中抛出的异常,并使用 traceback
模块获取堆栈跟踪信息。然后,装饰器会使用 raise
语句重新抛出异常,并将堆栈跟踪信息作为异常消息的一部分。这样,堆栈跟踪信息就会指向函数 bottom
,而不是函数 middle
。
上面就是我对于堆栈跟踪的一些理解,如果有任何不懂的可以评论区留言讨论,在实际应用中,异常处理方式可能因需求而异。有时候,简单地打印堆栈跟踪可能是一个调试工具,而在生产环境中,你可能会希望记录异常信息并采取适当的措施,例如发送警报或者回滚事务。