智泊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 True: c = 20 print(c)
import math print(math.e)
x = 3
def func(): y = 4 print(y) func()
def func2(): z = 5 def func3(): k = 6 print(k) func3()
def func4(): global a a = 100 print(a) func4() print(a)
A = 3 def func5(): A =4 def func6(): nonlocal A A = 5 print(f"func6中的A={A}") func6() print(f"func5中的A={A}")
func5() print(f"函数外的A:{A}")
|
2.函数嵌套和闭包
闭包 = 函数嵌套 + 外层返回内层函数。f1() 返回 f2,于是 fn22 = f1() 拿到的就是 f2 的实例,fn22() 才真正执行内层;fn22.__name__ 也证实了它指向的是 f2。关键在于:外层函数里的变量不会被释放,而是跟着内层函数一起被「封存」起来,这就是闭包能记住外部状态的原因。
1 2 3 4 5 6 7 8 9 10 11 12 13
|
def f1(): print("这是函数f1") def f2(): print("这是函数f2") return f2
fn22 = f1() fn22() print(fn22.__name__)
|
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)
print("-----------------")
def outer(fn): def inner(): print("先跳个舞") fn() print("再唱个歌") return inner
@outer 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
mysum()
|