【问题标题】:Variable scope difference between Python versions [duplicate]Python版本之间的变量范围差异[重复]
【发布时间】:2019-04-24 15:00:26
【问题描述】:

考虑以下代码:

import shutil
import time
t = time.time()
exception = None
while time.time() < (t + 10.0):
    try:
        shutil.rmtree('/path-to-non-existent-directory')
        break
    except OSError as exception:
        pass
    time.sleep(0.1)
else:
    if exception:
        raise exception

在 Python 2.7 中,此代码完全有效,但在 Python 3.7 中,我收到以下警告:

Local variable exception might be referenced before assignment

else 子句中。

有人知道在 Python 3.7 中运行时这个 sn-p 有什么问题吗?

【问题讨论】:

  • @ThomasWeller except OSError as exception: 不会将 exception 设置为不为假的东西吗?
  • this question 是相关的,但那里的答案都说在循环之前初始化变量应该可以防止警告。
  • @Eric Salemi - 我已经在 Python 3.7 中尝试过,它返回一个错误,提示“计时器”、“shutil”、“cls”和“时间”未定义。但在 Python 2.7 中,'Timer'(仅限)似乎没有定义。你应该初始化你的变量,以尽量减少错误。
  • @Eric Salemi - 添加一个“导入时间”来初始化“时间”变量。
  • 您究竟是如何收到该警告的?听起来更像是一个过分热心的短绒比其他任何东西。我无法简单地通过运行代码来重现该警告。

标签: python


【解决方案1】:

在 Python 3 中,为了解决由于引入 __traceback__ 属性而导致的循环引用问题,except 目标会在 except 块的末尾自动删除。它的行为就像你写的一样

except OSError as exception:
    pass
    del exception

这在PEP 3110中有记录。

如果你想保留异常对象,你应该把它保存到第二个变量中:

except OSError as exception:
    saved_exception = exception

exception 仍将被删除,但您可以在 except 块结束后使用 saved_exception 检查异常对象。

【讨论】:

  • 这个答案完美地解释了为什么原始代码在 Python 2.7 中没有显示警告。使用第二个变量名感觉有点冗长和骇人听闻,但我想我可以忍受。
【解决方案2】:

Python 是一种块作用域语言,您不能在定义它的块之外引用变量,也不能在更新该变量的块之外使用新值。

换句话说,您不能在except 块之外引用exception 变量的错误数据,如果您尝试这样做,则exception 变量的值将是None(您设置在顶层)。

尝试将else 块的内容移动到except 块并去掉exception = Noneif exception,如下所示:

timer = Timer(10.0)
while timer.alive:
  try:
    shutil.rmtree(cls.workspace)
    break
  except OSError as exception:
    raise exception
  time.sleep(0.1)

如果您不想出现致命错误,您可以使用 print() 函数而不是 raise 关键字:

timer = Timer(10.0)
while timer.alive:
  try:
    shutil.rmtree(cls.workspace)
    break
  except OSError as exception:
    print(exception)
  time.sleep(0.1)

这是另一个示例(不起作用):

def hello():
  message = "Hello World"

print(message)

因为它会引发以下错误:

NameError: name 'message' is not defined

注意:我建议不要调用您的异常 exception,因为有一个名为 Exception 的错误类,这样做可能会在以后导致混乱。

祝你好运。

【讨论】:

  • exception 在循环开始之前就被赋值了。
  • @chepner 感谢您指出这一点,我没有注意到。
  • Python 不是块作用域,引用exception 的问题与作用域无关。 except targets are deleted at the end of an except block in Python 3 解决由__traceback__ 属性引入的循环引用。
  • @LogicalBranch AFAIK 仅 defclass 关键字正在 Python 中创建新范围。任何其他关键字,例如 forwhilewith 都不会创建范围,因此在它们中创建的名称将在它们之外可用,而在它们之前创建的名称将在它们内部可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
  • 2013-04-12
  • 2019-12-08
  • 1970-01-01
  • 2020-08-14
  • 2015-12-13
相关资源
最近更新 更多