Discuss / Python / decorator

decorator

Topic source

KonoNA7

#1 Created at ... [Delete] [Delete and Lock User]

练习题:

def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kw):
        begin = time.time()
        r = fn(*args, **kw) # 获取返回值
        end = time.time()
        print('%s executed in %s ms' % (fn.__name__, 1000 * (end - begin)))
        return r
    return wrapper

在函数调用的前后打印出'begin call''end call'的日志:

def call(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('begin call')
        r = func(*args, **kw)
        print('end call')
        return r
    return wrapper

既支持@log又支持@log('execute')

def log(x):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s%s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    if isinstance(x, str):
        text = x + ' '
        return decorator
    else:
        text = ''
        return decorator(x) # 此时返回的是wrapper

return decorator(x) # 此时返回的是wrapper

请问此处在log无参的情况下为什么会返回wrapper? decorator接受一个func参数,若不是函数参数则wrapper中的func.name 无法打印,那log无参的情况传入decorator的参数是什么? 此时定义一个b() @log

def b(): print('123')

则b是如何由log演化而来?,即b = ?

谢谢大神了!

東NANIAN

#3 Created at ... [Delete] [Delete and Lock User]

举个简单的例子:

借用大佬代码

    def log(x):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s%s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    if isinstance(x, str):
        text = x + ' '
        return decorator
    else:
        text = ''
        return decorator(x) # 此时返回的是wrapper

需求:公司的人事部(str)和技术部(func)分别需要一个员工;

1.正常情况下,招聘两个人分别去各自的部门,互不干扰(也就是log携带的参数(员工)去人事部;调用函数时传递的func参数(员工)去技术部;); 2.然鹅,入职时,来人事部报道的只有一位员工,该怎么分配呢? 3.问询之后,发现他是(str),那他就是人事部的员工,后来的那个肯定是技术部的; 4.问询之后,发现他是技术部的,那么就让人事部先空缺text='',让他去技术部return decorator(x)

感谢回复,我理解的您的意思是: @log

def b(): print('123') 这段代码看似是log没传入任何参数,实际上是把b作为一个函数对象传入了log中,也就是说是return decorator(b)

不知对否?

東NANIAN

#5 Created at ... [Delete] [Delete and Lock User]

可以这么理解,但有点牵强。

调用这段代码的流程一般是这样的:

1.log带参数
@log('call')
def b():
    print('123')

log带参数的时候好理解,因为有两个参数,所以参数会依次传递,所以call传给了log(),text接收了call, b()传给了decorator(),func接收的就是b()

2.log 不带参数
@log
def b():
    print('123')

log不带参数的时候,也就是只传入了b(),但不知道是传给谁的参数(其实默认是传给log()的,参数会依次传嘛,也就是位置参数),

def sum(x, y):
    return x + y

sum(3, 5)
sum(5, 3)

# 虽然结果相同,但 x 和 y 传递的顺序不同,两次的 x 和 y 就不同

# 带参数和不带参数其实就像这样
sum(3, 5)
sum( , 5)

然鹅,log()接收了这个参数,decorator()却没有收到自己需要的参数,这时就会报错:‘都是必传参数,为什么给它不给我?’,怎么办?那就给text默认一个参数:text = '',然后必须把x 显示的传递(不显示传递,还是会默认为是 log 的参数)给decroator()return decroator(x),这里可以抽象的理解为:

@log('')
def b():
    print('123')

这样,它们都有了自己需要的参数,然后就愉快的执行了。

東NANIAN

#6 Created at ... [Delete] [Delete and Lock User]

其实这里只是简单的判断了,x不是 str,那它就是function,所以当x不是str时,就认为它是 func,而funcdecroator的参数而不是log所需要的str,所以必须显示的把funcdecroator,但因为log也需要一个必传参数,所以要给它指定一个算是默认参数的值text = ''

茅塞顿开!非常感谢!

祝您工作顺利,生活愉快!

東NANIAN

#8 Created at ... [Delete] [Delete and Lock User]

非大佬,只是一个相信“勤能补拙”有点编程经验的 Python 小白,愿意与大家分享学习的心得和经验,让我们共同进步。

fomiuna

#9 Created at ... [Delete] [Delete and Lock User]

为什么 text='' 叫默认参数的值?这只是一个格式吧。不是说 log 需要一个必传参数,而是参数类型改变了。。就像函数的复合,现在要对一次和两次复合都成立。所以不嫌麻烦的话完全可以把情况分清楚:

def log(x):
if isinstance(x, str):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (x, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
else:
    @functools.wraps(x)
    def wrapper(*args, **kw):
        print('%s():' % x.__name__)
        return x(*args, **kw)
    return wrapper

KonoNA7

#10 Created at ... [Delete] [Delete and Lock User]

回复楼上的@fomiuna 同学:

log不带参数时,需要一句text = ''是因为函数wrapper()里引用到了text这个变量,如果不事先定义就会报错。 另外,既然您都觉得您写的这段代码比较麻烦,那么为什么不参考一下楼里的另一种写法呢?实现效果是一样的:)


  • 1
  • 2

Reply