智泊Python--Day9

智泊Python–Day9

  Day9 继续函数主题,钻进「作用域」和「装饰器」。作用域决定了变量在哪能见到——Python 按 LEGB 顺序查找:Local(局部)→ Enclosing(外层函数)→ Global(全局)→ Built-in(内置)。global 声明要改全局变量,nonlocal 声明要改外层函数的变量。闭包是「函数返回函数、内层捕获外层变量」的自然产物,而装饰器就是基于闭包、在不改原函数代码的前提下给它前后「包」一层功能的语法糖。

 

1.函数作用域

  全局变量在函数内可读;局部变量出了函数就销毁。注意一个 Python 特有坑:**if / while / for 没有独立作用域**——在它们里面定义的变量,出了块照样能访问(和 JavaScript 不同)。global 把函数内的赋值指向全局变量;nonlocal 则指向「外层(非全局)函数作用域」里的那个变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 函数作用域:变量起作用的范围
# 函数有作用域

# 全局变量:在函数外部定义的变量,在函数内部也可以使用
a = 10
def func():
# 局部变量:退出函数后,变量就不存在了
b = 30
print(b)

func()

# if while for语句没有作用域
if True:
c = 20
print(c)

# 函数嵌套
# 内建作用域 B:Built-in 整个Python环境都可以使用
import math
print(math.e)

# 全局作用域
x = 3

# 函数作用域 E:Enclosing 函数内部的函数 闭包
def func():
y = 4 # 函数作用域
print(y)
func()

# 局部作用域 L:Local 函数内部的变量
def func2():
z = 5
def func3():
k = 6 # 局部作用域
print(k)
func3()


# 关键字
# global 全局变量
def func4():
global a
a = 100
print(a)
func4()
print(a)

# nonlocal 非全局变量
A = 3
def func5():
A =4
def func6():
nonlocal A # 申明是函数作用域下的变量
A = 5
print(f"func6中的A={A}") # 5
func6()
print(f"func5中的A={A}") # 5

func5()
print(f"函数外的A:{A}") # 3

 

2.函数嵌套和闭包

  闭包 = 函数嵌套 + 外层返回内层函数。f1() 返回 f2,于是 fn22 = f1() 拿到的就是 f2 的实例,fn22() 才真正执行内层;fn22.__name__ 也证实了它指向的是 f2。关键在于:外层函数里的变量不会被释放,而是跟着内层函数一起被「封存」起来,这就是闭包能记住外部状态的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 函数嵌套

# 闭包:函数嵌套,且返回内部函数,就会形成闭包(了解)
# 函数作用域中的变量x = 10 不会被释放,而是被保存在闭包中
def f1():
print("这是函数f1")
def f2():
print("这是函数f2")
return f2

fn22 = f1() # fn22 是 f2 函数的实例
fn22() # f2
print(fn22.__name__) # f2 说明fn22指向的是f2函数

 

3.装饰器

  装饰器的作用是不修改原函数,给它前面 / 后面追加功能。最朴素的做法是 run2(run) 把函数当参数传进去再包一层,但缺点是「调用方式变了」。用 @outer 语法糖就能保持原调用名——@outer 等价于 sleep2 = outer(sleep2):原函数的名字被替换成了装饰器返回的新函数(所以 sleep2.__name__ 会变成 inner)。下面还给了个实用练习:用装饰器计算函数运行耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 装饰器
# 作用是在其他函数的前面或后面添加功能,但是不修改原函数

def run():
print("我在跑步")

def run2(fn):
print("先跳个舞")
fn()
print("再唱个歌")

run2(run)

# 上面存在缺点,调用的方式会发生变化
# 不使用装饰器,调用的方式是run
# 使用装饰器,调用的方式是run2(run)

print("-----------------")

def outer(fn):
def inner():
print("先跳个舞")
fn()
print("再唱个歌")
return inner

# def sleep():
# print("我爱睡觉")

# sleep()
# print("-----------------")
# sleep = outer(sleep)
# print(sleep.__name__)
# sleep()

@outer # 添加装饰器的语法,等价于sleep2 = outer(sleep2)
def sleep2():
print("我爱睡觉2")
print(sleep2.__name__)
sleep2()

print("-----------------")

# 练习: 写一个装饰器,计算函数运行的时间
import time
def dec(fn):
def inner():
start = time.time()
fn()
end = time.time()
print(f"{fn.__name__}函数运行的时间:{end-start}")
return inner

@dec
def mysum():
s = 0
for i in range(10**6):
s += i
#print(s)

mysum()

智泊Python--Day9
https://one-null-pointer.github.io/2026/07/28/智泊Python--Day9/
Author
liaoyue
Posted on
July 28, 2026
传送口