0
点赞
收藏
分享

微信扫一扫

如何通俗易懂地理解Python装饰器?怎么使用?


目录

​​一、装饰器​​

​​1. 装饰器的定义​​

​​2. 装饰器的示例代码​​

​​3. 装饰器的语法糖写法​​

​​二、装饰器的使用​​

​​1. 装饰器的使用场景​​

​​2. 装饰器实现已有函数执行时间的统计​​

​​ 三、通用装饰器的使用​​

​​1. 装饰带有参数的函数​​

​​2. 装饰带有返回值的函数​​

​​3. 装饰带有不定长参数的函数​​

​​4. 通用装饰器​​

​​四、多个装饰器的使用​​

​​1. 多个装饰器的使用示例代码​​

​​五、带有参数的装饰器​​

​​1. 带有参数的装饰器介绍​​

​​ 六、类装饰器​​

​​1. 类装饰器的介绍​​

一、装饰器

1. 装饰器的定义

就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数

装饰器的功能特点:

  1. 不修改已有函数的源代码
  2. 不修改已有函数的调用方式
  3. 给已有函数增加额外的功能

2. 装饰器的示例代码

# 添加一个登录验证的功能
def check(fn):
def inner():
print("请先登录....")
fn()
return inner


def comment():
print("发表评论")

# 使用装饰器来装饰函数
comment = check(comment)
comment()

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的函数
# '''执行函数之后'''
# return inner

代码说明:

  • 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
  • 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

执行结果:

请先登录....
发表评论

3. 装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

# 添加一个登录验证的功能
def check(fn):
print("装饰器函数执行了")
def inner():
print("请先登录....")
fn()
return inner

# 使用语法糖方式来装饰函数
@check
def comment():
print("发表评论")


comment()

说明:

  • @check 等价于 comment = check(comment)
  • 装饰器的执行时间是加载模块时立即执行。

执行结果:

请先登录....
发表评论

二、装饰器的使用

1. 装饰器的使用场景

  1. 函数执行时间的统计
  2. 输出日志信息

2. 装饰器实现已有函数执行时间的统计

import time

# 装饰器函数
def get_time(func):
def inner():
begin = time.time()
func()
end = time.time()
print("函数执行花费%f" % (end-begin))
return inner


@get_time
def func1():
for i in range(100000):
print(i)


func1()

执行结果:

...
99995
99996
99997
99998
99999
函数执行花费0.329066

 三、通用装饰器的使用

1. 装饰带有参数的函数

# 添加输出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力计算--")
fn(num1, num2)

return inner


# 使用装饰器装饰函数
@logging
def sum_num(a, b):
result = a + b
print(result)


sum_num(1, 2)

运行结果:

--正在努力计算--
3

2. 装饰带有返回值的函数

# 添加输出日志的功能
def logging(fn):
def inner(num1, num2):
print("--正在努力计算--")
result = fn(num1, num2)
return result
return inner


# 使用装饰器装饰函数
@logging
def sum_num(a, b):
result = a + b
return result


result = sum_num(1, 2)
print(result)

运行结果:

--正在努力计算--
3

3. 装饰带有不定长参数的函数

# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
fn(*args, **kwargs)

return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value

for value in kwargs.values():
result += value

print(result)

sum_num(1, 2, a=10)

运行结果:

--正在努力计算--
13

4. 通用装饰器

# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result

return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value

for value in kwargs.values():
result += value

return result


@logging
def subtraction(a, b):
result = a - b
print(result)

result = sum_num(1, 2, a=10)
print(result)

subtraction(4, 2)

运行结果:

--正在努力计算--
13
--正在努力计算--
2

# 通用装饰器
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result

return inner

四、多个装饰器的使用

1. 多个装饰器的使用示例代码

def make_div(func):
"""对被装饰的函数的返回值 div标签"""
def inner():
return "<div>" + func() + "</div>"
return inner


def make_p(func):
"""对被装饰的函数的返回值 p标签"""
def inner():
return "<p>" + func() + "</p>"
return inner


# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
return "人生苦短"

result = content()

print(result)

代码说明:

  • 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程

五、带有参数的装饰器

1. 带有参数的装饰器介绍

带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,...)

错误写法:

def decorator(fn, flag):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner


@decorator('+')
def add(a, b):
result = a + b
return result

result = add(1, 3)
print(result)

执行结果:

Traceback (most recent call last):
File "/home/python/Desktop/test/hho.py", line 12, in <module>
@decorator('+')
TypeError: decorator() missing 1 required positional argument: 'flag'

代码说明:

  • 装饰器只能接收一个参数,并且还是函数类型。

正确写法:

在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。

# 添加输出日志的功能
def logging(flag):

def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner

# 返回装饰器
return decorator


# 使用装饰器装饰函数
@logging("+")
def add(a, b):
result = a + b
return result


@logging("-")
def sub(a, b):
result = a - b
return result

result = add(1, 2)
print(result)

result = sub(1, 2)
print(result)

 六、类装饰器

1. 类装饰器的介绍

装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

类装饰器示例代码:

class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn

# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("请先登陆...")
self.__fn()


@Check
def comment():
print("发表评论")


comment()

代码说明:

  • @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
  • 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
  • call方法里进行对fn函数的装饰,可以添加额外的功能。

执行结果:

请先登陆...
发表评论

举报

相关推荐

0 条评论