为了账号安全,请及时绑定邮箱和手机立即绑定

关于装饰器的副作用

装饰器不是这样起作用的吗:f = log(f),为什么函数名还会被修改?

正在回答

2 回答

我自己的理解:

1.以上面的log函数为例,log内部定义了fn函数,fn.__name__ 为 'fn'

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

当执行了f = log(f),因为log函数返回的也是一个函数(fn),所以f实际指向的函数(fn),f.__name__ 为 'fn'

你可以试下: f=log,此时,f指向的函数就是(log),f.__name__ 为 'log'

2.再进一步,以本题答案为例:

def performance(unit):
    def perf_decorator(f):
        @functools.wraps(f)
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2-t1)*1000 if unit == 'ms' else t2-t1
            print 'call %s() in %f %s'%(f.__name__,t,unit)
            return r
        return wrapper
    return perf_decorator

本质是 f = performance(unit)(f)

performance(unit) 返回 perf_decorator 函数,perf_decorator(f) 返回 wrapper函数,所以f指向wrapper函数,如果不加装饰器@functools.wraps(f),则f.__name__ 为 'wrapper'。

加了@functools.wraps(f)之后,functools.wraps函数会执行 wrapper.__name__  = f.__name__ 

所以最终f.__name__ 的值仍为'f',有点绕,这是我自己的理解。

3.你可以在编写带参数的那章执行如下代码看看:

# -*- coding: utf-8 -*-

import time

def performance(unit):
    def perf_decorator(f):
        def wrapper(*args, **kw):
            t1 = time.time()
            r = f(*args, **kw)
            t2 = time.time()
            t = (t2-t1)*1000 if unit == 'ms' else t2-t1
            print 'call %s() in %f %s'%(f.__name__,t,unit)
            return r
        return wrapper
    return perf_decorator

#@performance('ms') 不用装饰器
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

#手动装饰
factorial = performance('ms')(factorial)
print factorial(10)
print factorial.__name__


5 回复 有任何疑惑可以回复我~

啊啊啊

0 回复 有任何疑惑可以回复我~

举报

0/150
提交
取消
python进阶
  • 参与学习       255665    人
  • 解答问题       2949    个

学习函数式、模块和面向对象编程,掌握Python高级程序设计

进入课程

关于装饰器的副作用

我要回答 关注问题
意见反馈 帮助中心 APP下载
官方微信