闭包与装饰器

将函数作为参数

1
2
3
4
5
6
7
8
def add(x,y):
return x+y
def sub(x,y):
return x-y
def apply(func,x,y):
return func(x,y)
print apply(add,2,1)
print apply(sub,2,1)

将函数作为返回值

1
2
3
4
5
6
7
def outter():
def inner():
print 'Inside inner'
return inner
foo = outter()
print foo
foo()

闭包

Python支持一个叫做函数闭包的特性,用人话来讲就是,嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如x,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)

1
2
3
4
5
6
7
def outter():
x=1
def inner():
print x
return inner
foo = outter()
print foo.func_closure

闭包例子2

1
2
3
4
5
6
7
8
9
def outer(x):
def inner():
print x
return inner
print1=outer(1)
print2=outer(2)
print1()
print2()

闭包单独拿出来就是一个非常强大的功能, 在某些方面,你也许会把它当做一个类似于面向对象的技术:outer像是给inner服务的构造器,x像一个私有变量。使用闭包的方式也有很多:你如果熟悉python内置排序方法的参数key,你说不定已经写过一个lambda方法在排序一个列表的列表的时候基于第二个元素而不是第一个。现在你说不定也可以写一个itemgetter方法,接收一个索引值来返回一个完美的函数,传递给排序函数的参数key。

装饰器

装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def outter(some_func):
def inner():
print 'before some_func'
ret = some_func()
return ret+1
return inner
def foo():
print 'foo'
return 1
decorated = outter(foo)
print decorated()

我们可以认为变量decorated是函数foo的一个装饰版本,一个加强版本。事实上如果打算写一个有用的装饰器的话,我们可能会想愿意用装饰版本完全取代原先的函数foo,这样我们总是会得到我们的”加强版“foo。想要达到这个效果,完全不需要学习新的语法,简单地赋值给变量foo就行了

1
2
3
4
foo = outter(foo)
@outter()
def foo()

顺带一提,以上两种写法是等价的,下面算是一种语法糖写法。

*args 与 **kwargs

*args

只考虑参数位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def one(*args):
print args
one()
one(1,2,3)
def two(x,y,*args):
print x,y,args
two('a','b','c')
def add(x,y):
return x+y
lst = [1,2]
print add(*lst)

**kwargs

只考虑参数名称

1
2
3
4
5
6
7
8
9
10
11
def foo(**kwargs):
print kwargs
foo()
foo(x=3,y=4)
dct ={'x':1,'y':2}
def bar(x,y):
return x+y
print bar(**dct)

更通用的装饰器

函数运行时间计时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def timekeeper(func):
def inner(*args, **kwargs):
t0 = time.time()
print '%s, {%s} start' % (time.strftime("%H:%M:%S",time.localtime()), func.__name__)
ret = func(*args, **kwargs)
print '%s, {%s} end' % (time.strftime("%H:%M:%S",time.localtime()), func.__name__)
print '%.6fs taken for {%s}' % (time.time()-t0,func.__name__)
print '-------------------------------------'
return ret
return inner
@timekeeper
def one(*args):
print args
one()
one(1,2,3)

#