Python装饰器

【一】什么是装饰器

  • 装饰 代指为被装饰对象添加新的功能, 代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。
  • 概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
  • 装饰器经常用于有切面需求的场景
    • 插入日志、性能测试、事务处理、缓存、权限校验等应用场景
    • 装饰器是解决这类问题的绝佳设计
    • 有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

【二】装饰器的用途

  • 软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。
    • 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
    • 对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
  • 软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃
    • 而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

【三】装饰器的分类

  • 函数装饰器分为:无参装饰器和有参装饰两种
  • 二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。

【四】无参装饰器

【1】引入

import time def index(): time.sleep(3) print('Welcome to the index page’) return 200 index() #函数执行

【2】直接计时

  • 遵循不修改被装饰对象源代码的原则,我们想到的解决方法可能是这样
import time def index(): time.sleep(3) print("Welcome to the index page") return 200 start_time = time.time() index() # 函数执行 stop_time = time.time() print('run time is %s' % (stop_time - start_time))

【3】函数作为参数

import time def index(): time.sleep(3) print("Welcome to the index page") return 200 def wrapper(func): # 通过参数接收外部的值 start_time = time.time() res = func() stop_time = time.time() print('run time is %s' % (stop_time - start_time)) return res wrapper(index)

【4】将值包给函数

import time def index(): time.sleep(3) print("Welcome to the index page") return 200 def timer(func): # 通过参数接收外部的值 def wrapper(): start_time = time.time() res = func() stop_time = time.time() print('run time is %s' % (stop_time - start_time)) return inner wrapper = timer(index) wrapper()

【5】无参装饰器模版

def wrapper(func): def inner(): # 第一部分:执行外部传入的函数之前执行的代码 '''...''' # 第二部分:执行外部传入的函数地址(res接受外部函数的返回值,如果有返回值的情况下) result = func() # 第三部分:执行外部传入的函数之后执行的代码 '''...''' return result return inner

【五】有参装饰器

【1】有参装饰器详解

  • 之所以会抛出异常,是因为home(egon)调用的其实是wrapper(egon),而函数wrapper没有参数。
    • wrapper函数接收的参数其实是给最原始的func用的
    • 为了能满足被装饰函数参数的所有情况,便用上*args+**kwargs组合
    • 于是修正装饰器timer如下
import time def home(name): time.sleep(5) print('hello,world',name) def wrapper(func): def inner(*args,**kwargs): start_time = time.time() res = func(*args,**kwargs) stop_time = time.time() print('run time is %s' % (stop_time - start_time)) return inner home = wrapper(home) home("zhang")

【2】有参装饰器模版

# 通过参数 func 接收外部的函数地址 def wrapper(func): def inner(*args, **kwargs): # 第一部分:执行外部传入的函数之前执行的代码 '''...''' # 第二部分:执行外部传入的函数地址(res接受外部函数的返回值,如果有返回值的情况下) result = func(*args, **kwargs) # 第三部分:执行外部传入的函数之后执行的代码 '''...''' return result return inner

【六】无参语法糖

  • 此时我们就可以用timer来装饰带参数或不带参数的函数了
    • 但是为了简洁而优雅地使用装饰器,Python提供了专门的装饰器语法来取代index=timer(index)的形式
    • 需要在被装饰对象的正上方单独一行添加 @timer
    • 当解释器解释到 @timer 时就会调用timer函数
    • 且把它正下方的函数名当做实参传入
    • 然后将返回的结果重新赋值给原函数名
import time def home(name): time.sleep(5) print('Welcome to the home page', name) # 定义外层函数 def wrapper(func): # 定义内层函数:通过参数接收外部的值 def inner(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('run time is %s' % (stop_time - start_time)) return inner # 第一种调用方式 # 这里调用外层函数 wrapper 返回了内层函数的地址 inner home = wrapper(home) # 而内层函数其实是 inner(*args, **kwargs) # 所以我们可以传参数进去 home("Dream") # 第二种调用方式:语法糖 @wrapper def index(): time.sleep(5) print('Welcome to the index page') index()

【七】有参语法糖

# 通过参数 func 接收外部的函数地址 def wrapper(func): def inner(*args, **kwargs): # 第一部分:执行外部传入的函数之前执行的代码 '''...''' # 第二部分:执行外部传入的函数地址(res接受外部函数的返回值,如果有返回值的情况下) result = func(*args, **kwargs) # 第三部分:执行外部传入的函数之后执行的代码 '''...''' return result return inner

【八】伪装装饰器

import time from functools import wraps # 定义外层函数 def wrapper(func): # 伪装内层函数 @wraps(func) # 定义内层函数:通过参数接收外部的值 def inner(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('run time is %s' % (stop_time - start_time)) return inner @wrapper def home(name): ''' home page function :param name: str :return: None ''' time.sleep(5) print('Welcome to the home page', name) print(help(home)) ''' 打印结果: Help on function home in module __main__: home(name) home page function :param name: str :return: None None '''

__EOF__

本文作者Fredette
本文链接https://www.cnblogs.com/Fredette/p/17902002.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Fredette  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示