在if语句中初始化的变量作用域是什么?

406

这可能是一个简单的作用域问题。下面的代码在Python文件(模块)中让我有点困惑:

if __name__ == '__main__':
    x = 1
    
print x

在我工作过的其他编程语言中,这段代码会抛出异常,因为 x 变量只在 if 语句中存在,不应该在外部存在。但是这段代码执行了,并打印了1。有人能解释一下这种行为吗?在模块中创建的所有变量都是全局的/可用于整个模块吗?


30
你可能不知道的另一个怪癖是:如果上面的if语句不成立(即,当你导入模块而不是在顶层执行它时,__name__ 不是 '__main__'),那么x将永远没有被绑定,随后的print x语句将会抛出一个NameError: name 'x' is not defined异常。 - Santa
参见:https://dev59.com/J3VC5IYBdhLWcg3wcgud - Karl Knechtel
7个回答

508

Python变量的作用域限制在最内层的函数、类或模块中分配它们的作用域范围内。像ifwhile这样的控制结构不算,因此在if语句内分配的变量仍然是在函数、类或模块的作用域范围内。

(通过生成器表达式或列表/集合/字典推导定义的隐式函数算作作用域范围内的,lambda表达式也算。虽然你不能把赋值语句放到它们中间,但是lambda参数和for循环的目标是隐式赋值的。)


4
好的,下面是内容翻译:@chandr3sh https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces - Fakher Mokadem
7
感谢您的回答。我已经使用Python编码多年,直到现在才理解这一点。了解这一点确实使事情变得更容易了。我总是觉得Python处理作用域的方式有时很麻烦。 - AndreGraveler
类创建的作用域是临时的 - 它只存在于类本身被创建的时间,然后将内容转换为类对象的属性。例如,在方法代码中不能使用nonlocal访问这些名称。它们可以在self上找到(即使它是对象实例而不是类),但这是属性查找的特殊行为,而不是作用域解析。 - Karl Knechtel
在2.x版本中,推导式和生成器表达式不会创建“隐式函数”,或者至少不会为代码创建新的作用域;例如,迭代变量可以从列表推导式中“泄漏”出来,就像从for循环中一样。在3.8及以上版本中,“海象” := 运算符可以在推导式中使用,以将值分配给外部作用域中的变量:例如,在 x = 0之后,[_ for x in [1]] 不会修改外部的 x,但是 [x:=1 for _ in range(1)] 会。 - Karl Knechtel

168

是的,它们在同一个“局部作用域”中,实际上 Python 中常见这样的代码:

if condition:
  x = 'something'
else:
  x = 'something else'

use(x)

请注意,条件语句中的x并没有在声明或初始化之前,例如在C或Java中。

换句话说,Python没有块级作用域。但要小心例如:

if False:
    x = 3
print(x)

这显然会引发一个NameError异常。


4
我陷入了 if False: 的陷阱。 - steoiatsl
1
你知道吗:这在Python3.10中有所改变吗?更新后,我遇到了一些与作用域相关的错误。例如:if condition: i = 2 else: i = 0 for var in array: i += 1-> 这会导致TypeError:不支持的操作数类型'+=':'NoneType'和'int' - chakmear

48

Python中的作用域按照以下顺序搜索:

  • 搜索本地作用域

  • 搜索任何封闭函数的作用域

  • 搜索全局作用域

  • 搜索内建作用域

(来源)

请注意,if等循环/分支结构未列出 - 只有类、函数和模块在Python中提供作用域,因此在if块中声明的任何内容都与块外声明的内容具有相同的作用域。变量不会在编译时检查,这就是为什么其他语言会抛出异常的原因。在Python中,只要在需要时变量存在,就不会抛出异常。


16

与 C 等语言不同,Python 变量在它所在的整个函数(或类、模块)范围内都是可见的,而不仅限于最内层的“块”中。这就好像你在函数(或类、模块)顶部声明了 int x,只不过在 Python 中你无需声明变量。

请注意,变量 x 的存在只有在运行时才会进行检查 - 也就是当您到达 print x 语句时。如果 __name__ 不等于 "__main__",那么你将得到一个异常:NameError: name 'x' is not defined


类不会创建作用域;类中的“本地”变量只是在创建时添加到类的字典中。 - chepner

15

正如Eli所说,Python不需要变量声明。在C语言中,你需要这样写:

int x;
if(something)
    x = 1;
else
    x = 2;

但是在Python中,声明是隐含的,当您分配给x时,它会自动声明。这是因为Python是动态类型语言-在静态类型语言中无法工作,因为根据使用的路径,变量可能在未声明的情况下被使用。在静态类型语言中,这将在编译时被捕获,但是在动态类型语言中允许。

唯一的原因是静态类型语言受限于必须在if语句之外声明变量,因为有这个问题。拥抱动态语言!


6

是的。对于for语句的作用域也是如此。但当然不包括函数。

在你的例子中:如果if语句中的条件为false,则x将不会被定义。


3

你正在从命令行执行此代码,因此if条件为真,并且x被设置。请对比:

>>> if False:
    y = 42


>>> y
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    y
NameError: name 'y' is not defined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接