Python的一个重要语法特性是装饰器,它本身还提供了’@’这样优雅的语法糖使用装饰器。装饰器本质上是闭包的一个应用。Go支持闭包,也就可以说Go也支持装饰器,只是Go编译器提供的“糖”太小了,不能像Python一样优雅地使用装饰器。但,这并不阻碍我们使用装饰器这个方便的特性。

关于闭包

闭包是一个函数返回值,该返回值依然是一个函数。这个函数携带函数外部定义的变量。可以把这个外部变量理解为闭包的初始状态。

我们看看JavaScript的实现

1
2
3
4
5
6
7
8
9
10
function Counter() {
var n = 0;
return function incr() {
retrun n + 1;
}
}

var counter = Counter();
counter() // 1
counter() // 2

对比Python的实现

1
2
3
4
5
6
7
8
9
10
11
def Counter():
n = 0
def incr():
nonlocal n
n = n + 1
return n
return incr

c = Counter()
c() // 1
c() // 2

这样,内部函数incr都携带Counter函数内部的n状态。

一个简单的示例

关于装饰器,先来一个简单的示例:

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
package main

import (
"fmt"
)

func decorator(f func(s string)) func(s string) {
return func(s string) {
fmt.Println("start func...")
f(s)
fmt.Println("end func...")
}
}

func inner(s string) {
fmt.Println("I am inner function", s)
}

func test() {
fn := decorator(inner)
fn("one")
}

func main() {

test()
}

inner函数被decorator装饰过后返回fn。

和Python的比较

对比Python的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from functools import wraps

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("start func...")
func(*args, **kwargs)
print("end func...")
return wrapper

@decorator
def inner(s):
print("I am inner function", s)

def main():
inner()

对比下,Go就小’@’,但可以通过传入函数代替。’@’本身在Python中也是传入函数的语法糖。

Go装饰器的一个应用

使用装饰器来计算函数运算时间。

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
package main

import (
"fmt"
"time"
)

func fib(n uint) uint {
if n == 0 || n == 1 {
return 1
} else {
return fib(n-1) + fib(n-2)
}
}

type Fib func(n uint) uint

func timedFunc(f Fib) Fib {
return func(n uint) uint {
defer func(t time.Time) {
fmt.Println("elapsed time is ", time.Since(t))
}(time.Now())
return f(n)
}
}

func main() {
fib_timer := timedFunc(fib)
fmt.Println(fib_timer(30))
}

另外还可以实现计时、定时执行、HTTP装饰等,终止Python的装饰器技巧都可以搬到Go上,只是后者没有那么多”糖”。