学习笔记 | Python装饰器

装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。

以下的log函数就是一个装饰器:

import functools

def log(text): # ☀
    def decorator(func): # ☀
        @functools.wraps(func) # 这里是把原始函数的__name__等属性复制到wrapper()函数中
        def wrapper(*args, **kw): # ☀
            print('%s():%s' % (func.__name__,text))
            func(*args, **kw)
            print('%s():%s' % (func.__name__,'结束运行'))
        return wrapper
    return decorator

@log('进行加法计算') # 这是装饰器的语法糖,相当于这样写: count=log('进行加法计算')(count)
def count(num1,num2):
    print('{}+{}={}'.format(num1,num2,num1+num2))

count(1,7)

注意看三处“☀”,一共有三层函数相嵌套,每一层我们都传入了一些参数:最外层 log() 传入了装饰器的参数 text;第二层 decorator() 传入了一个函数,这个函数就是被装饰的函数;第三层 wrapper() 传入了被装饰的函数可能需要的参数。如此,这些参数都可以直接在 wrapper() 里调用。

在 wrapper() 里,我们在调用原函数的前后打印了一些文字。最后的输出结果:

count():进行加法计算
1+7=8
count():结束运行
[Finished in 1.0s]

上面是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。上面的代码还可以这样改写:

class Log():

	def __init__(self,text): # ☀
		self.text = text 

	def __call__(self,func): # ☀
		def wrapper(*args, **kw): # ☀
			print('%s():%s' % (func.__name__,self.text))
			func(*args, **kw)
			print('%s():%s' % (func.__name__,'结束运行'))

		return wrapper

@Log('进行加法计算')
def count(num1,num2):
    print('{}+{}={}'.format(num1,num2,num1+num2))

count(1,7)

显然,Log就是我们的类装饰器。类装饰器要用到两个魔法方法:__init__和__call__。

传入参数的方法换汤不换药,还是上面提到的三层。注意三个“☀”,第一个“☀”在__init__处传入了装饰器的参数 text ,第二个“☀”在__call__处传入了被装饰的函数,然后我们在__call__内部定义了一个函数 wrapper(),第三个“☀”在这里传入被装饰的函数可能需要的参数。

最后的效果一样。

如果装饰器不需要参数 text,则只用传入两层参数,上述两种装饰器分别可这样改写:

# 两层参数分别落脚__init__和__call__,不必在__call__里再定义一个函数
class Log():

	def __init__(self,func): # ☀
		self.func = func

	def __call__(self,*args,**kw): # ☀
		print('%s():%s' % (self.func.__name__,'开始运行'))
		self.func(*args,**kw)
		print('%s():%s' % (self.func.__name__,'结束运行'))

@Log
def count(num1,num2):
    print('{}+{}={}'.format(num1,num2,num1+num2))

count(1,7)
import functools

#将最外一层的log函数剥掉
def decorator(func): # ☀
    @functools.wraps(func)
    def wrapper(*args, **kw): # ☀
        print('%s():%s' % (func.__name__,'开始运行'))
        func(*args, **kw)
        print('%s():%s' % (func.__name__,'结束运行'))
    return wrapper

@decorator # 装饰器的语法糖,相当于: count=decorator(count)
def count(num1,num2):
    print('{}+{}={}'.format(num1,num2,num1+num2))

count(1,7)

原先 count() 的作用是打印一个算式,但如果我要它在打印完之后再返回一个值呢?——

​
.....

@decorator # 相当于: count=decorator(count)
def count(num1,num2):
    print('{}+{}={}'.format(num1,num2,num1+num2))
    return num1+num2

num = count(1,7)
print(num)

此时num的值是多少?是8吗?最后我们得到一个出乎意料的结果:None。为什么会这样呢?

由于我们对 count 使用了装饰器,调用 count 相当于在调用 decorator(count),而decorator(count)又会返回一个 wrapper 。因为我们没有给wrapper函数设置返回值,所以默认返回None。

为解决这个问题,我们应该把wrapper函数改成这样:

.....
def wrapper(*args, **kw):
    print('%s():%s' % (func.__name__,'开始运行'))
    f = func(*args, **kw)
    print('%s():%s' % (func.__name__,'结束运行'))
    return f
.....

这样一来,既没有影响装饰器的功能,又返回了原函数想返回的值。

参考资料:

装饰器 - 廖雪峰的官方网站 (liaoxuefeng.com)【Python】一文弄懂python装饰器(附源码例子)_攻城狮白玉的博客-CSDN博客​​​​​​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值