Python 在处理来自sys.exc_info()的回溯对象时应适当小心并确保安全

Python 在处理来自sys.exc_info()的回溯对象时应适当小心并确保安全,python,exception,error-handling,circular-reference,Python,Exception,Error Handling,Circular Reference,我知道文档中说在处理回溯对象时要小心,但我仍然不确定某些情况有多安全或不安全。此外,文档上写着“警告:不要这样做!”,紧接着是“注意:实际上,没问题”,这让我更加困惑 无论如何,docs和“”(Alex Martelli的答案)似乎暗示了它唯一的局部变量,这些变量引用了分配给它们的回溯值,从而导致了问题 这给我留下了几个问题: 在这种情况下,“局部变量”到底意味着什么?我正在努力寻找术语,但是:这是指只在函数中创建变量,还是也指由函数参数创建的变量?范围中的其他变量如何,例如全局变量或self

我知道文档中说在处理回溯对象时要小心,但我仍然不确定某些情况有多安全或不安全。此外,文档上写着“警告:不要这样做!”,紧接着是“注意:实际上,没问题”,这让我更加困惑

无论如何,docs和“”(Alex Martelli的答案)似乎暗示了它唯一的局部变量,这些变量引用了分配给它们的回溯值,从而导致了问题

这给我留下了几个问题:

  • 在这种情况下,“局部变量”到底意味着什么?我正在努力寻找术语,但是:这是指只在函数中创建变量,还是也指由函数参数创建的变量?范围中的其他变量如何,例如全局变量或self
  • 闭包如何影响回溯的潜在循环引用?一般的想法是:闭包可以引用其封闭函数所能引用的所有内容,因此引用闭包的回溯最终可能会引用很多内容。我正在努力想出一个更具体的例子,但有一些组合:一个内部函数,返回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()
    关闭是否会导致引用
    错误队列
    引发任何异常并导致循环引用,因为
    错误队列
    也包含回溯?从
    错误队列
    移除回溯(即调用
    .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()
    引发的异常是否引用了
    昂贵的\u对象
    ,并且由于
    昂贵的\u对象
    被缓存,回溯永远不会消失?如果是这样,如何打破这样的循环

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

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

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

    def func(fruwappah, qitzy=None):
        if fruwappah:
            fruit_cake = 'plain'
        else:
            fruit_cake = qitzy
        frosting = 'orange'
    
    变量
    fruwappah
    qitzy
    fruit_-cake
    frosting
    都是本地变量。哦,既然
    self
    在函数头中(在我的示例中不是这样),它也是本地变量

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

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

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

  • 定义一个在引发异常后将被调用的函数——它在回溯中没有堆栈帧,因此当它结束时,所有变量(包括回溯)都将消失;或者
  • 当您使用完对象后,请确保并
    del traceback\u object

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

    Ethan,如果您想捕获在另一个线程中引发的异常,情况如何?我遇到的矛盾是,线程必须存储异常(因此,回溯有一个指向自身存储位置的引用),这取决于线程调用方(或除ThreadWithException之外的任何人)。实际上,我想我找到了一个方法。你应该添加第三点:
    del
    任何包含回溯传递引用的对象。例如,
    try:func();除了:obj.exc=sys.exc_info();del obj
    。从检查
    gc.get_objects()
    可以看出,有问题的对象的refcount下降到0(
    obj
    ,exc_info),并打破循环,我应该澄清:通过检查回溯帧,不再有对
    obj
    的引用,因此
    obj
    引用回溯,但回溯不引用
    obj
    技巧是从回溯对象中提取所需的任何信息,然后去掉回溯对象项目。顺便说一句,哟