函数式编程 - [Python] 闭包函数和装饰器的疑问

查看:123
本文介绍了函数式编程 - [Python] 闭包函数和装饰器的疑问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

今天看书上的例子:

from time import ctime, sleep
def tsfunc(func):
    def wrappedFunc():
        print '[%s] %s() is called' % (ctime(),func.__name__)
        return func()
    return wrappedFunc

@tsfunc
def foo():
    pass
    
foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()    

输出的结果是好理解的:

eric@laptop ~/kuaipan/testcode $ python deco.py
[Tue Mar 15 22:46:32 2016] foo() called
[Tue Mar 15 22:46:37 2016] foo() called
[Tue Mar 15 22:46:38 2016] foo() called

我看了这个例子的第一念头产生的疑问是:
第三行的def wrappedFunc():和第六行的return wrappedFunc是多余的?
所以怀着这样的想法,试了一下:


from time import ctime,sleep

def tsfunc(func):
    #def wrappedFunc():
    print '[%s] %s() called' % (ctime(), func.__name__)
    return func
    #return wrappedFunc

@tsfunc
def foo():
    pass

foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()

结果,只返回了一次结果:

eric@laptop ~/kuaipan/testcode $ python deco.py
[Tue Mar 15 22:50:13 2016] foo() called
eric@laptop ~/kuaipan/testcode $

为了弄清楚发生了什么,我在控制台一行一行的执行代码,结果如下:

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from time import ctime,sleep
>>> def tsfunc(func):
...     print '[%s] %s() called' % (ctime(),func.__name__)
...     return func
... 
>>> @tsfunc
... def foo():
...     pass
... 
[Tue Mar 15 10:21:27 2016] foo() called
>>> foo
<function foo at 0x7f6416c885f0>
>>> foo()
>>> foo()
>>> print foo()
None

从过程看来,foo()只是在定义@tsfunc时被立刻装饰了一次,后边再调用foo()时就只是调用了foo本身了,这是为什么???

解决方案

整个程序很好理解,我们一步步来拆解:@tsfunc 其实等同于 foo = tsfunc(foo),因此你的程序等同于:

from time import ctime, sleep
def tsfunc(func):
    def wrappedFunc():
        print '[%s] %s() is called' % (ctime(),func.__name__)
        return func()
    return wrappedFunc

def foo():
    pass
    
foo = tsfunc(foo)

foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()    

此时对于foo = tsfunc(foo),由于tsfunc返回的是wrappedFunc,因此foo = tsfunc(foo)等同于 foo = wrappedFunc,需要注意的是tsfunc调用时并不会被打印任何语句,因为tsfunc的局部作用域内并无print,而只有foo()执行时才会打印wrappedFunc()内的print,所以你调用foo()三次,则打印了三次,因此最终这段程序可以等同于:

from time import ctime, sleep
def wrappedFunc():
    print '[%s] %s() is called' % (ctime(),func.__name__)
    return pass   # 原来 foo() 的结果就是 pass

def foo():
    pass
    
foo = wrappedFunc

foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()    

而当去除第三行和第六行后,情况就不一样了,首先仍然改写下 @tsfunc,程序为:

from time import ctime,sleep

def tsfunc(func):
    print '[%s] %s() called' % (ctime(), func.__name__)
    return func

def foo():
    pass

foo = tsfunc(foo)

foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()

对于foo = tsfunc(foo) 由于tsfunc(foo)最终返回的是传入的参数foo,所以foo = tsfunc(foo)等价于foo = foo,而print其实只是在@tsfunc被调用那次,执行了一次打印,由于foo本身本就只有一个'pass'因此不管你下面调用多少次foo(),只会输出tsfunc被调用那次的 print,因此最终程序等同于:

from time import ctime,sleep

def foo():
    pass
    
print '[%s] %s() called' % (ctime(), func.__name__)

foo = foo

foo()
sleep(4)

for i in range(2):
    sleep(1)
    foo()

其实你的示例涉及到装饰器和闭包函数的内容,对于这两者的作用可以参看这篇文章:
Python 的闭包和装饰器

这篇关于函数式编程 - [Python] 闭包函数和装饰器的疑问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆