9

我知道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

闭包是否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()

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

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

4

1 回答 1

4

在这种情况下,“局部变量”究竟是什么意思?我正在为术语而苦苦挣扎,但是:这是否意味着仅在函数中创建的变量,还是由函数参数创建的变量?范围内的其他变量呢,例如全局变量或自我?

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

def func(fruwappah, qitzy=None):
    if fruwappah:
        fruit_cake = 'plain'
    else:
        fruit_cake = qitzy
    frosting = 'orange'

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

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

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

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

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

说了这么多,我还需要在我自己的代码中使用回溯对象——到目前为止,异常及其各种属性已经足够了。

于 2011-09-11T07:27:49.083 回答