卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

    • Python函数参数是值传递还是引用传递?
    • 位置参数、关键字参数、*args和**kwargs怎么用?
    • Python默认参数为什么不能随便使用列表和字典?
    • Python中的匿名函数、map、filter和reduce怎么用?
    • Python的LEGB作用域是什么?
      • 简要回答
      • 详细回答
      • 知识拓展
    • global和nonlocal有什么区别?
    • 什么是闭包?闭包中的变量为什么会出现延迟绑定?
    • Python模块和包是什么?import是怎么工作的?
  • 面向对象

  • 进阶语法与语言机制

# Python的LEGB作用域是什么?

面试官问:"Python如何查找变量?什么是LEGB规则?"

很多人只知道"局部变量和全局变量",但说不清楚嵌套函数的作用域、为什么有时会报UnboundLocalError,以及Built-in作用域包含什么。

# 简要回答

Python使用LEGB规则查找变量,按以下顺序依次搜索:

  • L(Local):当前函数的局部作用域。
  • E(Enclosing):外层函数的局部作用域(闭包)。
  • G(Global):模块的全局作用域。
  • B(Built-in):内置作用域,包含len、print等内置函数和异常。

查找时从内向外,找到第一个匹配的名字就停止。如果四层都找不到,抛出NameError。

赋值默认创建局部变量。如果函数内对一个名字赋值,Python认为它是局部变量,查找时只在局部作用域找;如果赋值前尝试读取,会抛出UnboundLocalError。

# 详细回答

# 四层作用域

x = "global"  # Global作用域

def outer():
    y = "enclosing"  # Enclosing作用域
    
    def inner():
        z = "local"  # Local作用域
        print(z)  # local,找到L
        print(y)  # enclosing,L找不到,找E
        print(x)  # global,L和E找不到,找G
        print(len)  # <built-in function len>,L、E、G找不到,找B
    
    inner()

outer()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

inner函数查找变量时:

  1. 先在自己的局部作用域(L)找。
  2. 找不到,到外层函数outer的作用域(E)找。
  3. 还找不到,到模块全局作用域(G)找。
  4. 最后到内置作用域(B)找。

LEGB作用域查找链

# Built-in作用域

内置作用域包含Python自动导入的所有内置函数、异常和常量:

print(__builtins__.__dict__.keys())
# dict_keys(['__name__', '__doc__', 'abs', 'all', 'any', ..., 'len', 'print', 'range', ...])
1
2

可以在任何地方使用len、print、range等,不需要导入,因为它们在Built-in作用域中。

# Enclosing作用域与闭包

嵌套函数可以访问外层函数的局部变量:

def outer():
    count = 0
    
    def inner():
        print(count)  # 访问外层函数的局部变量
    
    inner()  # 0

outer()
1
2
3
4
5
6
7
8
9

inner的Enclosing作用域是outer的局部作用域。这是闭包的基础。

# 赋值创建局部变量

函数内对名字赋值,Python认为它是局部变量:

x = 10

def func():
    x = 20  # 创建局部变量x,覆盖全局x
    print(x)  # 20

func()
print(x)  # 10,全局x未被修改
1
2
3
4
5
6
7
8

# UnboundLocalError陷阱

x = 10

def func():
    print(x)  # UnboundLocalError: local variable 'x' referenced before assignment
    x = 20

func()
1
2
3
4
5
6
7

为什么会报错?因为函数内对x赋值,Python在编译时认为x是局部变量。执行print(x)时,局部作用域中x还没有绑定值,所以抛出UnboundLocalError。

解决方法:用global声明(见下一篇文章《global和nonlocal有什么区别?》)。

# 全局变量只读不写

函数内可以读取全局变量,但不能修改:

count = 0

def increment():
    count = count + 1  # UnboundLocalError

increment()
1
2
3
4
5
6

右侧的count需要先读取值,但左侧的count是赋值操作,Python认为count是局部变量。在赋值前读取局部变量,抛出UnboundLocalError。

# 类定义不引入作用域

类定义内的名字不在函数的查找链中:

class MyClass:
    x = 10
    
    def method(self):
        print(x)  # NameError: name 'x' is not defined

obj = MyClass()
obj.method()
1
2
3
4
5
6
7
8

method查找x时,按LEGB顺序查找,不会查找类命名空间。必须写成self.x或MyClass.x。

# 列表推导式的作用域

Python 3中,列表推导式有自己的局部作用域:

x = "global"
result = [x for x in range(3)]
print(x)  # global,推导式内的x不影响外部
1
2
3

推导式的循环变量是局部的,不会泄漏到外层作用域。

# 知识拓展

Q1:可以在程序中动态修改作用域吗?

不能直接修改作用域链,但可以用locals()和globals()访问作用域字典:

def func():
    x = 10
    print(locals())  # {'x': 10}

x = 20
print(globals()['x'])  # 20
1
2
3
4
5
6

不推荐动态修改globals()返回的字典,可读性差且容易出错。

Q2:del删除变量后,外层同名变量会暴露出来吗?

不会。del删除当前作用域的名字,但不会让外层同名变量在当前作用域可见:

x = 10

def func():
    x = 20
    del x
    print(x)  # NameError: name 'x' is not defined

func()
1
2
3
4
5
6
7
8

删除x后,局部作用域中不再有x,查找时会向外层查找,但如果函数内某处对x赋值,Python在编译时就认为x是局部变量,删除后再访问会报错。

Q3:内置函数能被覆盖吗?

可以,但不推荐:

len = 100  # 覆盖内置函数len
print(len([1, 2, 3]))  # TypeError: 'int' object is not callable
1
2

全局作用域的len覆盖了Built-in作用域的len。删除后恢复:

del len
print(len([1, 2, 3]))  # 3
1
2

Q4:嵌套多层时,查找会跨越多个Enclosing作用域吗?

会:

def outer():
    x = "outer"
    
    def middle():
        def inner():
            print(x)  # outer,逐层向外查找
        inner()
    
    middle()

outer()
1
2
3
4
5
6
7
8
9
10
11

inner查找x时,先查Local,再查middle的Enclosing,再查outer的Enclosing,最后在outer的局部作用域找到。

Q5:LEGB规则在模块导入时适用吗?

适用。导入模块后,模块名是全局作用域的名字;模块内的名字在模块自己的全局作用域中。

Last Updated: 10/13/2026, 5:57:00 PM

← Python中的匿名函数、map、filter和reduce怎么用? global和nonlocal有什么区别? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪