装饰器(让你的函数更强)

装饰器

https://www.todaybing.com/detail/NTIHGrxD.html

我们知道,在python里面,我们可以像变量一样使用函数:
1.函数可以被赋值给其他的变量
2.函数可以被删除
3.函数的里面还可以定义函数,就是我们上面说的闭包
4.函数可以被作为一个函数的参数被传递,就是高阶函数
5.函数可以作为另一个函数的返回
6.整体的说起来,函数就是一个对象

对一个简单的函数进行装饰

为了更好的说明这一切,我们先写一个最简单的函数来进行一步步的深入:

def hello():
return 'Hello world!'

此时我们写了一个最简单的函数出来.我们做到装饰器的本意就是在不改变源代码的样子就能加强它的功能,假如我们想增强上面的函数的功能,希望在返回函数的时候,不是孤孤单单的就返回一个字符串,想给他加一个HTML的标签.就像这样**hello**.但是有个要求就是不改变原函数的样子.

def makeitalic(func):
def wrapped():
return "<i>+func()+"</i>
return wrapped

在上面,我们将hello()函数传给makeitalic()放的,再讲函数返回给hello(),此时的hello()函数还是存在的,但是它的函数名字已经不是hello()了,而是wrapped()这个函数.好啦~我们来停下来看看我们现在做了什么.为了增强原函数的功能,我们重新写了新的函数,将原函数作为新函数的参数,并返回一个新的函数. 事实上,这个makeitalic就是一个装饰器(decorator),一般在python里面,我们看见的是@这个语法糖,语法糖就是简化写法使用的:

def makeitalic(func):
def wrapped():
return "<i>+func()+"</i>
return wrapped

@makeitalic
def hello():
return 'Hello world!'

像上面这些情况,可以动态的修改函数(类)功能的函数就是一个装饰器.本质上是一个高阶函数,以被装饰的函数作为参数,返回一个包装后的函数.

装饰器的使用形式

一般使用的形式

@decorator
def func():
pass

等价于下面的形式:

def func():
pass
func = decorator(func)

装饰器可以定义多个,离函数定义最近的装饰器最先被调用

@decorator_one
@decorator_two
def func():
pass

等价于:

def func():
pass
func = decorator_one(decorator_two(func))

装饰器还可以带参数

@decorator(arg1, arg2)
def func():
pass

等价于

def func():
pass
func = decorator(arg1, arg2)(func)

对带参数函数进行装饰

一般来说,函数带参数才是常态,我们来写一写:

def makeitalic(func):
def wrapped(*args,**kwargs):
ret = func(*args,**kwargs)
return "<i>+func()+"</i>
return wrapped

@makeitalic
def hello(name):
return 'hello %s' % name

@makeitalic
def hello(name1,name2):
return 'hello %s, %s' % (name1,name2)

由于函数hello带参数,因此内嵌的包装函数wrapped也做一些改变:

内嵌的包装函数的参数传给了func,即被装饰的函数,也就是说内嵌包装函数的参数跟被装饰的函数的参数对应,后面的写法是适应可变参数.

带参数的装饰器

上面的例子,我们增强了函数hello()的功能,给它返回值加上了...的标签,现在我们还想加...,等等的标签.那再写一个?不现实是肯定的一个回答,那我们是不是可以写一个函数,想加的标签做参数,返回一个装饰器,我们来写一下:

def wrap_in_tag(tag):
def decorator(func):
def wrapped(*args,**kwargs):
ret = func(*args,**kwargs)
return '<' + tag +'>'+'</' + tag + '>'
return wrapped
return decorator

现在我们生成:

makebold = wrap_in_tag('b')

@makebold
def hello(name):
return 'Hello %s' % name

hello('world')

写的再干净点

@wrap_in_tag('b')
def hello(name):
return 'hello %s' % name

其实就是再装饰器的外面加了一层包装,根据不同的参数返回不同的装饰器

多个装饰器

def makebold(func):
def wrapped():
return '<b>' + func+'</b>'
return wrapped

def makeitalic(func):
def wrapped():
return '<i>' + func+'</i>'
return wrapped
@makeitalic
@makebold
def hello():
return 'hello world'

相当于什么?

def hello():
return 'hello world'
hello = makebold(makeitalic(hello))

基于类的装饰器

装饰器前面是基于函数,其实也可以基于类定义

class Bold(object):
def __init__(self,func):
self.func = func

def __call__(self,*args,**kwargs):
return '<b>' + self.func(*args,**kwargs) + '</b>'

@Bold
def hello(name):
retuen 'hello %s' % name

hello('hello')

可以看到有两个方法:
1.init()他接受一个函数作为参数,也就是被装饰的函数
2.call()让类对象可调用,就像函数调用一样,再调用被装饰函数的时候被调用.

装饰器的副作用

注意使用装饰器有个毛病,就是被装饰的函数,它的函数名称已经不是原来的名称了.为了消除这个毛病,让我们来这样做:

from functools import wraps

def makeitalic(func):
@wraps(func)#加上装饰器
def wrapped():
return "<i>+func()+"</i>
return wrapped

@makeitalic
def hello():
return 'hello world'

hello.__name__

小结

1.本质上,装饰器就是iyge返回函数的高阶函数
2.可以动态的修改类以及函数的功能,通过再原有的类上面包裹函数完成
3.其实,装饰器是闭包的一种应用,区别是装饰器一定会返回一个函数.闭包没有这种限制

(0)

相关推荐