由于前几天了解到了 python 的装饰器,感觉有点点类似于 AOP 思想,因此记录一下学习笔记。
封面《金色ラブリッチェ》

闭包 (closure)

在讲 python 装饰器前,需要先介绍一下闭包

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.

直接展示例子

PYTHON  
1
2
3
4
5
6
7
8
9
10
def f():
    x=1
    def g(y):
        return x + y
    # 返回闭包
    return g  

closure = f()
# 输出3
print(closure(2))

本来在 f() 执行完后,x 就会被内存回收,但是内部函数使用了 x,因此 x 内存不会被回收。
闭包的好处在于函数内的参数不会被内存回收,可以隐藏一些内容,同时避免了命名冲突。缺点在于内存无法自动回收,容易溢出。

装饰器 (decorator)

先来看看一个简单的装饰器例子

PYTHON  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
# 外部
def timer(func):
    #内部
    def wapper(*args,**kwargs):
        start = time.time()
        ans = func(*args,**kwargs)
        end = time.time()
        print(end - start)
        return ans
    return wapper
@timer
def loop(nums):
    for i in range(nums):
        pass
    print("over")
loop(55555555)
print(loop.__name__)

其输出结果如下

SHELL  
1
2
3
over
1.4170827865600586
wapper

其调用方式拆分为如下几步

PYTHON  
1
2
wapper = timer(loop)
wapper(55555555)

可以看到我们使用了一个装饰器来增强了 loop 方法,为其添加了计算耗时的功能。外部函数传进去的是函数,内部函数传进的是参数。args 和 kwargs 可以看。简单来说就是 args 是以元组传参,kwargs 是以键值对来传参。
另外这种实现装饰器的方法很简单,但是也有缺陷,就是__name__属性发生了变化。这对一些使用__name__的库来说有缺陷。可以使用自带 functools 库来完成。

修复__name__

PYTHON  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
import functools

def timer(func):
    @functools.wraps(func)
    def wapper(*args,**kwargs):
        start = time.time()
        ans = func(*args,**kwargs)
        end = time.time()
        print(end - start)
        return ans  
    return wapper

@timer
def loop(nums):
    for i in range(nums):
        pass
    print("over")
loop(55555555)
print(loop.__name__)

其输出结果如下

SHELL  
1
2
3
over
1.1916275024414062
loop

functools.warps (func) 将原函数的__module__,__name__,__doc__,__dict__等拷贝 func 中。
可以看到使用 functools 后__name__信息变成了原函数的名字

带参数的装饰器

PYTHON  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import time
import functools
def timer_with_parm(name):
    def timer(func):
        @functools.wraps(func)
        def wapper(*args,**kwargs):
            print("this is:"+name)
            start = time.time()
            ans = func(*args,**kwargs)
            end = time.time()
            print(end - start)
            return ans
        return wapper
    return timer
@timer_with_parm("timer1")
def loop(nums):
    for i in range(nums):
        pass
    print("over")
loop(55555555)
print(loop.__name__)

其输出结果如下

SHELL  
1
2
3
4
this is:timer1
over
1.2349653244018555
loop

其调用方式拆分为如下几步

PYTHON  
1
2
3
timer = timer_with_parm("timer1")
wapper = timer(loop)
wapper(55555555)

可以看到 decorator 中的参数已经被传入了,且通过 wapper 函数输出了。

总结

python 的装饰器实现了 AOP 模型,简化了代码

参考

[1] 闭包 wiki
[2]args 和 kwargs
[3] 理解 Python 装饰器 (Decorator)