目 录
建议初学者在看本章之前,先看一下这两个小节,有经验的同学请随意。
Python 函数式编程 Functional Programming-上
Python 函数式编程 Functional Programming-下
关于装饰器的总共三篇,分别是:
Python 装饰器
定义:是一种可以装饰其它对象的工具,本质上是一个可调用的对象(callable),所以装饰器一般可以由函数、类来实现。
用途:装饰器本身需要接受一个被装饰的对象作为参数,该参数通常为函数、方法、类等对象。
返回值:装饰器需要返回一个对象,该对象可以是经过处理的原参数对象、一个包装且类似原参数的对象;或者返回一个不相干内容。
Python 装饰器类别,积累中.........
装饰器来源 | 装饰器类别 | 实现原理 |
用户自定义 | 函数 | 函数的闭包特性 |
类 | 一般依靠类内部的__call__方法,可以修饰类、函数,分无参数和有参数两种 | |
内置 | @property @x.setter @x.getter @x.deleter | 特性装饰器 |
@classmethod | 类方法装饰器 | |
@staticmethod | 静态方法装饰器 |
1. 用户自定义的装饰器-函数
1.1 无参装饰器
1.1.1 被修饰的函数没有参数
装饰器、被装饰函数都不带参数,这是一种最简单的情况。个人觉得实际运行中绝大多数被修饰的函数都是有参数的。
先说一个非常简单的装饰器,它甚至没有定义新函数,但是确实实现了装饰器的功能
def wrap(func):
print("Happy birthday!")
return(func)
@wrap
def f1():
print("Everyone is OK !")
f1()
稍微复杂一点的如下:
# 定义装饰器函数 logger
def logger(func):
#定义一个嵌套函数
def wrapper(*args, **kw):
print("*"*50)
print("Function begin!")
# 真正执行的是这行。
func()
print("Function end!")
return wrapper
@logger
def f1():
print("This is function 1 !")
f1()
运行结果:
1.1.2 被修饰的函数有参数
被修饰的函数有参数,下面是一个简单的例子,不知道大家注意到没有,f1 函数是有返回值的,但是被装饰之后,却没有返回值。
# 定义装饰器函数 logger
def logger(func):
#定义一个嵌套函数
def wrapper(*args, **kw):
print("*"*50)
print("Function begin!")
# 真正执行的是这行。
func(*args, **kw)
print("Function end!")
return wrapper
@logger
def f1(a,b,c=3):
print("This is function 1 !")
res=a+b+c*10
print(res)
return(res)
print("+"*20) #第一个被执行的语句
a=f1(100,50,1) #接下来执行的语句
print("-"*20) # 最后执行的语句
print(a) # 可以看到被装饰后,函数返回值是 None,原因在于 wrapper 函数中没有写返回值。
运行结果
如果大家注意到 f1() 被装饰后缺少返回值,那是因为闭包函数 wrapper 中只是执行了被当作参数传进来的函数,并没有把执行结果传递出去,简单如下修改一下即可。
# 定义装饰器函数 logger
def logger(func):
#定义一个嵌套函数
def wrapper(*args, **kw):
print("*"*50)
print("Function begin!")
# 真正执行的是这行。
res=func(*args, **kw) #先保存函数返回值
print("Function end!")
return(res) #此处将 func 返回值传递出去
return wrapper
@logger
def f1(a,b,c=3):
print("This is function 1 !")
res=a+b+c*10
print(res)
return(res)
print("+"*20) #第一个被执行的语句
a=f1(100,50,1) #接下来执行的语句
print("-"*20) # 最后执行的语句
print(a) # 这时候被装饰的函数返回值也被传递出来
1.2 有参装饰器
这时候要对原有装饰器进行封装,并返回一个装饰器,即一个含有参数的闭包函数。
1.2.1 被修饰的函数没有参数
对 1.1.1 的代码进行修改。
# 定义装饰器函数 logger
def logger(flag):
def show(func):
def wrapper(*args, **kw):
if(flag==True):
print("flag is True,We use * to show!")
print("*"*50)
else:
print("flag is False,We use + to show!")
print("+"*50)
print("Function begin!")
# 真正执行的是这行。
func()
if(flag==True):
print("flag is True,We use * to show!")
print("*"*50)
else:
print("flag is False,We use + to show!")
print("+"*50)
print("Function end!")
return(wrapper)
return(show)
@logger(True)
def f1():
print("This is function 1 !")
@logger(False)
def f2():
print("This is function 2 !")
f1()
f2()
1.2.1 被修饰的函数有参数
假如装饰器有参数,被装饰的函数也有参数,那么其实很简单,相对于 1.2.1 的代码,只需要改动一行代码即可。
# 定义装饰器函数 logger
def logger(flag):
def show(func):
def wrapper(*args, **kw):
if(flag==True):
print("flag is True,We use * to show!")
print("*"*50)
else:
print("flag is False,We use + to show!")
print("+"*50)
print("Function begin!")
# 真正执行的是这行。
func(*args, **kw) #~~~~~~~~~~~~~改这一行即可~~~~~~~~~~
if(flag==True):
print("flag is True,We use * to show!")
print("*"*50)
else:
print("flag is False,We use + to show!")
print("+"*50)
print("Function end!")
return(wrapper)
return(show)
@logger(True)
def f1(a,b):
print("This is function 1 !")
print(a+b)
@logger(False)
def f2(a,b,c,d):
print("This is function 2 !")
print(a*b+c+d)
f1(100,99)
print("---分割线---"*5)
f2(100,3,20,1)
2. 用户自定义的装饰器-类
2.1 类装饰器实现原理-特殊的类方法 __call__
Python 中有一个非常特殊的类方法 __call__,我们知道一般的用户定义函数、内置函数、方法都是可调用 callable 对象,那么类实例是不是可调用 callable 对象呢。只要这个类实现了 __call__ 方法,它就是可调用 callable 对象,否则不是。换句话说,我们可以把这个类型的对象当作函数来使用,相当于 重载了括号运算符。
先看看没有实现方法 __call__的类,可以用内置函数 callable() 去判断它的实例是否是可调用 callable 对象。
class Man():
def __init__(self,name,age):
self.name=name
self.age=age
'''
def __call__(self,name,age):
print("Name is ",name)
print("Age is ",age)
'''
man=Man("Ivy",39)
callable(man)
如果实现了 方法 __call__,情况就不一样了
class Man():
def __init__(self,name,age):
self.name=name
self.age=age
def __call__(self,name,age):
print("Name is ",name)
print("Age is ",age)
man=Man("Ivy",39)
callable(man)
在这个时候,就可以使用 Man 类的实例实现函数的功能,这是实现类装饰器的原理,如下:
2.2 简单的没有参数的类装饰器
2.2.1 被装饰的函数没有参数
下面是一个简单的,没有参数的类装饰器,用来装饰一个普通的没有参数的函数。
# 定义装饰器函数 logger
class Logger(object):
def __init__(self, f):
self.f = f
def __call__(self):
print("Logger start")
self.f()
print("Logger end")
@Logger
def func1():
print("func1")
@Logger
def func2():
print("func2")
func1()
func2()
运行结果如下:
2.2.1 被装饰的函数有参数
只需要简单的在 __call__ 类方法中加上参数即可
# 定义装饰器函数 logger
class Logger(object):
def __init__(self, f):
self.f = f
def __call__(self,*args, **kw):
print("Logger start")
self.f(*args, **kw)
print("Logger end")
@Logger
def func1(a,b):
print("func1")
print(a+b)
@Logger
def func2(a,b,c,d):
print("func2")
print(a*b*c-d)
func1(100,99)
func2(2,3,11,5)
2.3 带有参数的类装饰器
类装饰器带有参数时候稍微复杂一点
# 定义装饰器函数 logger
class Logger(object):
def __init__(self, level):
self.level = level
def __call__(self,func,*args, **kw):
def wrapper(*args, **kwargs):
print("[{0}]: enter {1}()".format(self.level, func.__name__))
return func(*args, **kwargs)
return wrapper
@Logger(True)
def func1(a,b):
print("func1")
print(a+b)
@Logger(False)
def func2(a,b,c,d):
print("func2")
print(a*b*c-d)
func1(100,99)
func2(2,3,11,5)
'''
要是大家觉得写得还行,麻烦点个赞或者收藏吧,想个博客涨涨人气,非常感谢!
'''