python之装饰器和闭包

装饰器与闭包的学习

装饰器

初识装饰器

装饰器(decorator)实际上是一个函数,用来改变其他函数的功能,如果我们有一个函数,功能为打印一句话
本文的代码格式炸了,我也很无力,明明是对的

1
2
def f1():
print("hello, I am f1.")

现在我们需要改变打印的内容,但是不能修改此函数中的打印语句,那么我们可以添加一个函数,修改f1这个引用指向的对象为新的函数

1
2
3
4
5
def decorator(func):
def inner():
print("hello, I am NOT f1.")
return inner
f1 = decorator(f1)

运行f1,得到的结果如下

1
2
>>> f1()
hello, I am NOT f1.

可以看到,我们成功地改变了原函数的打印语句,上面的decorator函数即为python中的装饰器,我们可以使用语法糖@将上面的python代码简化

1
2
3
@decorator
def f1():
print("hello, I am f1.")

Python何时执行装饰器

装饰器在模块导入时即执行,而不是显示调用被装饰的函数时才执行,下面我们来看一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
registry = []

def register(func):
print('running register(%s)' % func)
registry.append(func)
return func

@register
def f1():
print('running f1')

@register
def f2():
print('running f2')

if __name__ == '__main__':
f1()
f2()

如果上面的py文件当做脚本运行,那么可以得到结果:

1
2
3
4
running register(<function f1 at 0x100f962a8>)
running register(<function f2 at 0x100f961b8>)
running f1
running f2

如果是当做模块导入,那么结果为:

1
2
running register(<function f1 at 0x100f962a8>)
running register(<function f2 at 0x100f961b8>)

这也证实了装饰器在模块导入时运行

装饰器的功能

从上面的例子可以看到,我们可以将工程中的register函数定义为装饰器,其他功能函数在使用之前必须要经过装饰器注册后才能使用,在需要临时取消某些函数功能时,只需要去掉语法糖”@”即可,功能强大且非常灵活、

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
registry = []

def register(func):
print('running register(%s)' % func)
registry.append(func)
return func

# 启用f1
@register
def f1():
print('running f1')

# 启用f2
@register
def f2():
print('running f2')

# 禁用f3
def f3():
print('running f3')

def manager():
for func in registry:
"""do something"""
pass

在上面的例子中,f1和f2两个功能函数启用,f3被禁用,manager函数实现对所有启用的函数进行操作。

Python变量的作用域

python变量遵循”LEGB”原则,L(local),E(enclosing),G(global),B(built-in),即解释器在寻找变量时,会现在函数内部(local)里寻找,如果找不到,去外层函数(enclosing)中寻找,最后去全局变量(global)和内建(built-in)中寻找,下面看一个例子

1
2
3
4
5
b = 1
def f1(a):
print(a)
print(b)
f1(2)

显然,最终的输出结果为

1
2
2
1

这是因为解释器把b当做了全局变量打印,现在我们稍作修改

1
2
3
4
5
6
b = 1
def f1(a):
print(a)
b = 3
print(b)
f1(2)

现在的输出结果为

1
2
2
3

现在的变量b是局部(local)变量,解释器优先在local里寻找,而不是global,我们再加修改

1
2
3
4
5
6
b = 1
def f1(a):
print(a)
print(b)
b = 3
f1(2)

输出结果为

1
2
3
UnboundLocalError: local variable 'b' referenced before assignment

这是因为此时的b在编译阶段被确认为局部变量,但是我们未经赋值就进行使用,所以会报错!

闭包

休息一下,喝杯咖啡,继续创作