为什么 Python 中的 with 块不需要 global 关键字?

Why doesn't the with block in Python need the global keyword?

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

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 的值。

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

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

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

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