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() counter()
|
对比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上,只是后者没有那么多”糖”。