[Python] 闭包函数和装饰器的疑问

发布于 2022-09-02 10:41:20 字数 1763 浏览 21 评论 0

今天看书上的例子:

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本身了,这是为什么???

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

旧城空念 2022-09-09 10:41:20

整个程序很好理解,我们一步步来拆解:@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 的闭包和装饰器

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文