Python 中的迭代器是什么?
Python 的作用域(scope)这个东西啊,说难也不难,说简单也能被玩出花来,尤其是面试的时候,一道“Python 的作用域解析是什么?”足以劝退一半人。别问我怎么知道的,问就是伤心往事。
那我们就从我熟悉的老套路开始聊。Python 的作用域解析遵循一个经典模型——LEGB,也就是:
Local(本地作用域) Enclosing(闭包作用域) Global(全局作用域) Built-in(内置作用域)
这玩意看起来像背单词,其实理解了也不难。就像写代码时变量名冲突了,Python 解释器就会按这个顺序一层层往外找。
就像你深夜加班写代码,找不到变量,Python 就会从你写的函数里找(Local),再找外层函数(Enclosing),再找模块级别的变量(Global),最后才翻翻自带的字典(Built-in)。
先看个简单的例子:
x = "global"
defouter():
x = "enclosing"
definner():
x = "local"
print(x)
inner()
outer()
运行结果是 local,因为 x 在 inner() 里面被定义了,解释器就近找到了变量,没继续往外找。就像你领导就在你旁边说“今晚必须交”,你还会去找 CEO 问 deadline 嘛?
但如果 inner() 里面不定义 x,那就会去外面找:
x = "global"
defouter():
x = "enclosing"
definner():
print(x)
inner()
outer()
这次输出是 enclosing,因为它找不到 local,就去 enclosing 找了。如果 enclosing 也没有呢?那就 global,最后才 builtin。
那有人就问了:“我在函数里想改 global 变量,咋办?”你得声明一下啊,用 global 关键字:
x = "global"
defmodify():
global x
x = "changed"
modify()
print(x) # 输出:changed
不声明的话,Python 会以为你是创建了个 local 变量,结果你还以为自己在改 global,实际上改了个 lonely local,可怜巴巴的没人用。
同理,如果你在闭包里要修改 enclosing 层的变量,就得用 nonlocal:
defouter():
x = "enclosing"
definner():
nonlocal x
x = "changed"
inner()
print(x)
outer() # 输出:changed
不加 nonlocal 的话,x = "changed" 这句会新建一个 local 变量,不会改 enclosing 的变量,很多人都卡在这里。
这时候面试官可能来个骚操作:给你写个 lambda,然后问你值是怎么传的,闭包变量是怎么绑定的。
funcs = []
for i in range(3):
funcs.append(lambda: i)
for f in funcs:
print(f())
新手会以为输出是 0, 1, 2,但其实是 2, 2, 2。因为 lambda 里的 i 是晚绑定的(late binding),等到 f() 被调用时,i 已经变成 2 了。
正确姿势:
funcs = []
for i in range(3):
funcs.append(lambda i=i: i)
for f in funcs:
print(f()) # 输出:0 1 2
这样就把当前 i 绑定到 lambda 参数里了。
再多说一句,有些面试官爱考你装 x:“你知道 Python 的作用域为什么要这么设计吗?”
我觉得这就是 Python 想让你代码更清晰,作用域递归向外,逻辑结构分明,不像JavaScript 搞个 var 变量能在函数外玩飞。
Python 把“不要轻易改别人作用域的值”这条规矩写死在语法里了,非得改就得老实交代 global 或 nonlocal,不声明还真不让你乱来,妥妥的硬核。
所以如果你问我 Python 的作用域解析到底是啥,我会说它是个看起来简单,其实细节藏刀的东西。
要真掌握好,还得上手敲、自己调试几个例子才行。就像写代码一样,有时候以为自己懂了,真跑起来才知道哪里变量没作用上。
也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。
对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。