【问题标题】:Why doesn't the with block in Python need the global keyword?为什么 Python 中的 with 块不需要 global 关键字?
【发布时间】:2020-06-12 15:07:01
【问题描述】:

我正在使用with 块从文件中提取信息,我很惊讶with 块中声明的任何变量都是全局变量。它们不应该是 with 块的本地,并且在更改全局变量之前您不应该使用 global myVar 吗?毕竟with定义了一个代码块吧?

【问题讨论】:

  • 考虑if...else 块和循环,例如whilefor

标签: python python-3.x global-variables with-statement


【解决方案1】:

Python 没有块作用域。 with 语句没有引入新的作用域;语句的主体仍在与 with 语句相同的范围内。

Python 有 4 种作用域:

  1. 内置范围,定义任何模块中可用的名称,无需显式 import
  2. 全局范围,每个模块一个。
  3. 非本地范围
  4. 本地范围:由函数定义。

没有其他构造定义新范围:不是if 语句,不是forwhile 循环,不是with 语句,不是try 语句,不是class 语句。只有定义新函数(def 语句、lambda 表达式和理解)的事物才会创建新的(本地)范围。

每个名称首先在本地范围内查找(如果不在函数定义内,则可能是全局范围),然后在任何非本地范围内(如果函数定义在全局范围内,则可能不存在,而不是另一个本地范围),然后是全局范围,最后是内置范围。


非本地范围只是不是当前本地范围的范围。对于在全局范围内定义的模块,最近的封闭非局部范围全局范围。

但是如果一个函数是在另一个函数内部定义的,那么最近的封闭非本地范围就是定义该函数的本地范围。

函数可以嵌套得相当深,因此在当前本地范围和可以进行名称查找的全局范围之间可能有 0 个或多个额外的本地范围。例如,

x1 = 'a'

def f1():
    x2 = 'b'
    def f2():
        x3 = 'c'
        def f3():
            x4 = 'd'
            print(x1 + x2 + x3 + x4)
        f3()
    f2()

f1()

这个混乱的输出将是abcd。当print 语句的参数需要四个变量中的每一个的值时,每次查找都在本地范围内开始。在那里只能找到x4 的值;其他查找扩展到最近的封闭非本地范围,即f2。在那里找到了x3 的值,因此x1x2 的查找扩展到next 非本地范围,即f1。找到了x2,所以f1 又扩展了一个站点。最后,在全局范围内找到了x1 的值。

因此,非本地作用域与其说是一种特殊的作用域,不如说是对当前正在执行的函数来说不是局部作用域的名称。它要么是封闭函数的本地范围,要么是全局范围。

【讨论】:

  • 那么唯一有新作用域的块是函数?
  • 正确。不过,需要注意的两件事是class 语句和理解。 class 语句定义了一个新的 namespace,但没有定义一个新的范围。推导式是作为匿名函数实现的,因此它们定义了一个您可能没有想到的新范围。例如,[int(x) for x in some_list]x 定义为执行迭代的匿名函数的本地函数。 x 不再在列表之外的范围内(顺便说一句,在 Python 2 中不是。)
  • 是的,这与使用范围的 for 循环相同,对吧?像for i in range(10) 一样,i 没有保存一个全局变量,对吧?
  • 它可能不是全局的,但for 循环不会创建新范围。 i 属于包含 for 语句的任何作用域,这意味着在循环完成后仍会定义 i
  • @chepner 您应该将您对课程和理解的评论添加到您已经很好的答案中。
【解决方案2】:

with 语句不创建范围(如 if, for and while 也不创建范围)。

因此,Python 将分析代码并查看您在 with 语句中进行了赋值,因此将进行变量赋值。

with 范围之前的初始化可以更安全,因为在 with 语句之后我们可以安全地假设变量存在。另一方面,如果变量应该在 with 语句中赋值,而不是在 with 语句之前初始化它实际上会导致额外的检查:如果在 with 语句中以某种方式跳过了赋值,Python 将出错。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 2020-05-20
    • 1970-01-01
    • 2011-01-21
    相关资源
    最近更新 更多