从sys.exc_info()处理回溯对象时的适当注意和安全性

Ric*_*eur 9 python error-handling exception circular-reference

我知道sys.exc_info文档说在处理回溯对象时要小心,但我仍然不确定某些情况下的安全性或不安全性.此外,文档说"警告:不要这样做!",紧接着是"注意:实际上,它确定",这进一步让我感到困惑.

在任何情况下,文档和" 为什么需要在Python中显式删除sys.exc_info()回溯? "(Alex Martelli的回答),似乎暗示它唯一的局部变量引用分配给它们的追溯值,一个问题.

这给我留下了一些问题:

  1. 确切地说,"局部变量"在这种情况下意味着什么?我正在努力学习术语,但是:这是指仅在函数中创建的变量,还是由函数参数创建的变量?那么范围内的其他变量呢,比如全局变量还是自我变量?
  2. 闭包如何影响回溯的潜在循环引用?一般的想法是:一个闭包可以引用它的封闭函数可以的所有东西,所以带有对闭包的引用的回溯最终会引用很多.我正在努力想出一个更具体的例子,但是它的一些组合:一个内部函数,返回sys.exc_info()的代码,以及某个范围内的昂贵的短寿命对象.

随意告诉我我的结论或假设在哪里是错误的,因为我已经多次因为我已经写了这个问题而将自己的信念和不相信我自己的陈述推理出来了:).

虽然我想要回答我的具体例子,但我也要求提供关于如何在更深奥的情况下安全地处理追溯的一般建议,知识或战争故事(例如,你必须运行循环并想要累积任何引发异常,你必须生成一个新线程,并且需要报告任何引发的异常,你必须创建闭包和回调,并且必须回复引发的异常等.

示例1:执行错误处理的内部函数

def DoWebRequest():
  thread, error_queue = CreateThread(ErrorRaisingFunc)
  thread.start()
  thread.join()
  if not error_queue.empty():
    # Purposefully not calling error_queue.get() for illustrative purposes
    print 'error!'

def CreateThread(func):
  error_queue = Queue.Queue()
  def Handled():
    try:
      func()
    except Exception:
      error_queue.put(sys.exc_info())
  thread = threading.Thread(target=Handled)
  return thread, error_queue
Run Code Online (Sandbox Code Playgroud)

Handled()闭包是否导致引发任何引发的异常error_queue并导致循环引用,因为error_queue还包含回溯?从error_queue(即调用.get())中移除回溯足以消除循环引用?

示例2:exc_info范围内的长生命对象,或返回exc_info

long_lived_cache = {}

def Alpha(key):
  expensive_object = long_lived_cache.get(key)
  if not expensive_object:
    expensive_object = ComputeExpensiveObject()
    long_lived_cache[key] = expensive_object

  exc_info = AlphaSub(expensive_object)
  if exc_info:
    print 'error!', exc_info

def AlphaSub(expensive_object):
  try:
    ErrorRaisingFunc(expensive_object)
    return None
  except Exception:
    return sys.exc_info()
Run Code Online (Sandbox Code Playgroud)

引发的异常是否AlphaSub()有引用expensive_object,并且,因为expensive_object缓存,回溯永远不会消失?如果是这样,那么如何打破这样的循环呢?

或者,exc_info包含Alpha堆栈帧,Alpha堆栈帧包含引用exc_info,从而产生循环引用.如果是这样,那么如何打破这样的循环呢?

Eth*_*man 4

在这种情况下,“局部变量”到底意味着什么?我正在努力寻找术语,但是:这是否意味着仅在函数中创建的变量,或者也意味着由函数参数创建的变量?范围内的其他变量(例如全局变量或 self)怎么样?

“局部变量”是在函数内部创建的所有名称绑定。这包括任何函数参数以及分配的任何变量。例如在以下内容中:

def func(fruwappah, qitzy=None):
    if fruwappah:
        fruit_cake = 'plain'
    else:
        fruit_cake = qitzy
    frosting = 'orange'
Run Code Online (Sandbox Code Playgroud)

变量fruwappahqitzyfruit_cakefrosting都是局部变量。哦,因为self是在函数头中(当它是时,只是不在我的示例中;),它也是本地的。

闭包如何影响回溯的潜在循环引用?一般的想法是:闭包可以引用其封闭函数可以引用的所有内容,因此引用闭包的回溯最终可能会引用相当多的内容。我正在努力想出一个更具体的例子,但是一些组合:内部函数、返回 sys.exc_info() 的代码、在某个范围内的昂贵的短期对象。

正如您链接到的答案所述:回溯引用了异常发生时处于活动状态的每个函数(及其变量)。换句话说,是否涉及闭包是无关紧要的——分配给闭包(非本地),或者就此而言,全局变量将创建循环引用。

有两种基本方法可以解决这个问题:

  1. 定义一个在引发异常后调用的函数——它在回溯中不会有堆栈帧,因此当它结束时,它的所有变量——包括回溯——都会消失;或者
  2. 确保del traceback_object完成后。

话虽如此,我还需要在自己的代码中使用回溯对象——到目前为止,Exception 及其各种属性已经足够了。