python closures and decorators

Functions as objects and closures

python中的函数是first-class objects,即它们可以作为参数传递到其他函数中,可以存储到数据结构中,也可以作为函数的返回值

一个简单例子

# foo.py
def callf(func):
    return func()
>>> import foo
>>> def hello():
...     return ‘hello‘
...    
>>> f = foo.callf(hello) # function as an arg
>>> f
‘hello‘

对上面代码做一点修改:

# foo.py
x = 42
def callf(func):
    return func()
>>> import foo
>>> x = 37
>>> def hello():
...     return ‘hello. x is %d‘ % x
...
>>> foo.callf(hello)
‘hello. x is 37‘

这里我们调用foo.callf(),但是x的值却不是foo.py中定义的变量值,而是调用函数前上文的值。因此我们应该关注一个函数与周边变量的关系,也就有了closure闭包的概念

 

closure: 当一个函数的语句和它们所执行的环境一起打包的时候,就成为了所谓的closure 我们可以查看一个函数的__globals__属性,如:

>>> hello.__globals__
{‘__builtins__‘: <module ‘__builtin__‘ (built-in)>,
‘helloworld‘: <function helloworld at 0x7bb30>,
‘x‘: 37, ‘__name__‘: ‘__main__‘, ‘__doc__‘: None
‘foo‘: <module ‘foo‘ from ‘foo.py‘>}

可以看到hello里面已经绑定了x=37

当使用嵌套函数时,闭包会给inner function捕获执行需要的环境,如

import foo
def bar():
    x = 13
    def helloworld():
        return "Hello World. x is %d" % x
    foo.callf(helloworld) # returns ‘Hello World, x is 13‘

这里内部函数helloworld与x=13已经组成一个闭包

闭包可以怎样使用?我们看一个延时赋值的例子:

from urllib import urlopen
def page(url):
    def get():
        return urlopen(url).read()
    return get
>>> python = page("http://www.python.org")
>>> python
<function get at 0x95d5f0>
>>> pydata = python() # fetch http://www.python.org
>>> pydata.__closure__
(<cell at 0x67f50: str object at 0x69230>,)
>>> python._ _closure_ _[0].cell_contents
‘http://www.python.org‘

pydata对象在赋值的时候已经绑定了参数url的值,可以留到以后进行调用

Decorators

decorator装饰器的作用是在里面封装其他函数或者类,用@号来表示,eg:

@trace
def square(x):
    return x*x

等同于

def square(x):
    return x*x
square = trace(square)

这个例子中定义了一个square()函数,但马上将它作为参数传递到trace()中,并将返回的对象替代原来的square。装饰器顾名思义就是给原来函数增加一些装饰,比如我们这里trace函数可以做一些额外的事情,最后返回的对象可以是一个包含square的函数,即对我们的square函数做了“装饰”

再看一个平常使用的例子:

enable_tracing = True
if enable_tracing:
    debug_log = open("debug.log","w")

def trace(func):
    if enable_tracing:
        def callf(*args,**kwargs):
            debug_log.write("Calling %s: %s, %s\n" % (func._ _name_ _, args, kwargs))
            r = func(*args,**kwargs)
            debug_log.write("%s returned %s\n" % (func._ _name, r))
            return r
        return callf
    else:
        return func

这个代码就可以追踪上面square函数的调用,如果enable_tracing,则用函数callf将func封装起来,在里面增加记录日志功能,并且callf最后还是返回func函数,也就是说如果允许追踪的时候我们就将原来的函数对象装饰成一个有追踪功能的callf函数对象返回,如果不允许追踪,就简单返回func本身,这是非常精妙的一种设计

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。