Python实战基础13-装饰器

news2024/11/29 4:41:55

1、先明白这段代码

'''第一波'''
def foo():
    print('foo')

foo # 表示是函数
foo() # 表示执行foo函数

'''第二波'''
def foo():
    print('foo')

foo = lambda x: x + 1

foo() # 执行lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数

函数名仅仅是个变量,只不过指定了定义的函数而已,所以才能通过 函数名() 调用,如果 函数名=xxx被修改了,那么当在执行 函数名()时,调用的就不是之前的那个函数了

2、需求来了 

'''
装饰器:
遵循开放封闭原则,在不改变原函数的情况下,扩展了函数得功能

定义:
def xxx(func):
    def xx(参数,...):
        ...
        func()
        ...
        return yyy
    :return xxx
装饰:
@装饰器名   ----> 原函数 = 装饰(原函数)
def 原函数():
    pass
'''
# 定义装饰器
def decorater(func):
    print('---------1')
    def wrapper():
        func()
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房子')
    print('----------2')
    return wrapper

@decorater # house = decorater(house)
def house():
    print('毛坯房。。。')

house()

运行结果:

 

初创公司有N个业务部门,基础平台部门负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。

def f1():
    print('f1')

def f2():
    print('f2')

def f3():
    print('f3')

def f4():
    print('f4')


##################业务部门A调用基础平台提供的功能##########

f1()
f2()
f3()
f4()

##################业务部门B调用基础平台提供的功能###########

f1()
f2()
f3()
f4()

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。 

只对基础平台的代码进行重构,其他业务部门无需做任何修改

def check_login():
    #验证1
    #验证2
    #验证3
    pass

def f1():
    check_login()
    print('f1')

def f2():
    check_login()
    print('f2')

def f3():
    check_login()
    print('f3')

def f4():
    check_login()
    print('f4')

写代码要遵循 开放封闭 原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的功能代码块
  • 开发:对扩展开发

如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3、f4的内部进行修改代码,另一个实现方案

def w1(func):
    def inner():
        #验证1
        #验证2
        #验证3
        func()
    return inner

@w1
def f1():
    print('f1')
@w1
def f2():
    print('f2')
@w1
def f3():
    print('f3')
@w1
def f4():
    print('f4')

对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数f1 、f2、

f3、f4之前都进行【验证】操作,并且其他业务部门无需做任何操作。

详细讲解,单独以f1为例:

def w1(func):
    def inner():
        #验证1
        #验证2
        #验证3
        func()
    return inner

@w1
def f1():
    print('f1')

python解释器会从上到下解释代码,步骤如下:

  1. def w1(func):  ===>将w1函数加载到内存
  2. @w1

没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

从表面上看解释器着实会执行这两句,但是@w1这一句代码里却大有文章,@函数名 是python的一种语法糖。

上例@w1内部会执行以下操作:

执行w1函数

执行w1函数,并将@w1下面的函数作为w1函数的参数,即:@w1等价于w1(f1)所以,内部就会去执行:

def inner():
    #验证1
    #验证2
    #验证3
    f1()   # func是参数,此时func等于f1
return inner # 返回的inner,inner代表的是函数,非执行函数,其实就是将原来的f1函数塞进另外一个函数中

w1的返回值

将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名f1 即将w1的返回值在重新赋值给f1,即:

新f1 = def inner():
            #验证1
            #验证2
            #验证3
            原来f1()
        return inner()

所以,以后业务部门想要执行f1函数时,就会执行 新f1 函数,在新f1函数内部先执行验证,在执行原来的f1函数,然后将原来f1函数的返回值返回给了业务调用者。

如此一来,即执行了验证的功能,有执行了原来f1的函数的内容,并将原f1函数返回值返回给业务调用者。

3、再议装饰器

#定义函数:完成包裹数据
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

#定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
def test1():
    return "hello world-1"

@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test2():
    return "hello world-2"

@makeBold
@makeItalic
def test3():
    return "hello world-3"

print(test1())
print(test2())
print(test3())

运行结果:

4、装饰器(decorator)功能 

  1. 引入日志
  2. 函数执行时间统计
  3. 执行函数前预备处理
  4. 执行函数后清理功能
  5. 权限校验等场景
  6. 缓存

5、装饰器示例

5.1 无参数的函数

def check_time(action):
    def do_action():
        action()
    return do_action

@check_time
def go_to_bed():
    print('去睡觉')

go_to_bed()

上面代码理解装饰器执行行为可理解成

result = check_time(go_to_bed)  # 把go_to_bed 当做参数传入给 check_time函数,再定义一个变量用来保存check_time的运行结果
result()  # check_time 函数的返回值result是一个函数, result()再调用这个函数,让它再调用go_to_bed函数

5.2 被装饰的函数有参数

def check_time(action):
    def do_action(a,b):
        action(a,b)
    return do_action

@check_time
def go_to_bed(a,b):
    print('{}去{}睡觉'.format(a,b))

go_to_bed("zhangsan","床上")
'''
带参数的装饰器
如果原函数有参数则装饰器内部函数也要有参数
'''

def decorater(func):
    def wrapper(address): # address = '北京四合院'
        func(address) # func就是house
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房子')
    return wrapper

@decorater
def house(address):
    print('房子的地址在:{}是一个毛坯房...'.format(address))

house('北京四合院') # house 就是wrapper

5.3 被装饰器的函数有不定长参数

def test(cal):
    def do_cal(*args,**kwargs):
        cal(*args,**kwargs)
    return do_cal

@test
def demo(*args):
    sum = 0
    for x in args:
        sum += x
    print(sum)

demo(1,2,3,4)
def decorater(func):
    def wrapper(*args,**kwargs): # address = '北京四合院'
        # args 是一个元组
        func(*args,**kwargs) # func就是house  func(*('北京沙河北大桥‘,1000))
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房子')
    return wrapper

@decorater
def house(address):
    print('房子的地址在:{}是一个毛坯房...'.format(address))

@decorater
def changfang(address,area):
    print('房子的地址在:{}是一个毛坯房,建筑面积是:{}平米'.format(address,area))

@decorater
def hotel(name,address,area=40):
    print('酒店的名字是:{},酒店的地址在:{},单间建筑面积是:{}平米'.format(name,address,area))


house('北京四合院') # house 就是wrapper

print('-----------------')

changfang('北京沙河大桥',1000) # changfang 就是wrapper

print('-----------------')
hotel(name='全季大酒店',address='北京国贸')

5.4 装饰器中的return

def test(cal):
    def do_cal(*args,**kwargs):
        return cal(*args,**kwargs) # 需要在这里写return语句,表示调用函数,获取函数的返回值并返回
    return do_cal

@test
def demo(a,b):
    return a + b

print(demo(1,2))  # 3
'''
装饰器修饰有返回值函数
原函数有返回值,装饰器的内部函数也要有返回值
'''
def decorater(func): # function 函数
    def wrapper(*args,**kwargs):
        r = func(*args,**kwargs)
        print('预计装修费用是:{}元'.format(r))
        print('刷漆')
        print('铺地板')
        print('买家具')
        print('精装修房子')
        return 60000

    return wrapper

@decorater
def house():
    print('我是一个毛胚房。。')
    return 50000

r = house() # house 就是wrapper
print(r)

运行结果:

 

总结:一般情况下为了让装饰器更通用,可以有return

5.5 装饰器带参数 

def outer_check(time):
    print('----->1')
    def check_time(action):
        print('--------3')
        def do_action():
            if time < 22:
                return action()
            else:
                return '对不起,您不具有该权限'
        print('--------4')
        return do_action
    print('-----------2')
    return check_time

@outer_check(23)
def play_game(): # r = outer_check(23) r = check_time ---> check_time(play_game) ---->paly_game = do_action
    return '玩游戏'

print(play_game)

运行结果:

 

5.6 提高:适用装饰器实现权限验证

def outer_check(base_permission):
    def check_permission(action):
        def do_action(my_permission):
            if my_permission & base_permission:
                return action(my_permission)
            else:
                return '对不起,您没有权限'
        return do_action
    return check_permission

READ_PERMISSION = 1
WRITE_PERMISSION = 2
EXECUTE_PERMISSION = 4

@outer_check(base_permission=READ_PERMISSION)
def read(my_permission):
    return '读取数据'

@outer_check(base_permission=WRITE_PERMISSION)
def write(my_permission):
    return '写入数据'

@outer_check(base_permission=EXECUTE_PERMISSION)
def execute(my_permission):
    return '执行程序'

print(read(5))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/576514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

攻不下dfs不参加比赛(九)

标题 为什么练dfs题目为什么练dfs 相信学过数据结构的朋友都知道dfs(深度优先搜索)是里面相当重要的一种搜索算法,可能直接说大家感受不到有条件的大家可以去看看一些算法比赛。这些比赛中每一届或多或少都会牵扯到dfs,可能提到dfs大家都知道但是我们为了避免眼高手低有的东…

Python入门(十三)函数(一)

函数&#xff08;一&#xff09; 1.函数概述2.函数定义2.1向函数传递信息2.2实参和形参 作者&#xff1a;xiou 1.函数概述 函数是带名字的代码块&#xff0c;用于完成具体的工作。要执行函数定义的特定任务&#xff0c;可调用该函数。需要在程序中多次执行同一项任务时&#…

win10微软Edge浏览器通过WeTab新标签页免费无限制使用ChatGPT的方法,操作简单,使用方便

目录 一、使用效果 二、注册使用教程 1.打开Edge浏览器扩展 2.选择Edge浏览器外接程序 3.搜索WeTab 4.进入管理扩展 5.启用扩展 ​编辑 6.进入WeTab新标签页 7.打开Chat AI 8.注册 9.使用 ChatGPT是OpenAI推出的人工智能语言模型&#xff0c;能够通过理解和学习人类…

opencv_c++学习(二十五)

一、Harris角点介绍 1、海瑞斯角点不可能出现在图像平滑的区域&#xff08;上图1&#xff09;&#xff1b; 2、图像边缘的支线出不可能出现海瑞斯角点&#xff08;上图2&#xff09;&#xff1b; 3、海瑞斯角点会出现在顶点处。&#xff08;上图3&#xff09;&#xff1b; 上图…

一文带你了解MySQL之redo日志

前言 本文以及接下来的几篇文章将会频繁的使用到我们前边唠叨的InnoDB记录行格式、页面格式、索引原理、表空间的组成等各种基础知识&#xff0c;如果大家对这些东西理解的不透彻&#xff0c;那么阅读下边的文字可能会特别的些费力&#xff0c;为保证您能正常的理解&#xff0…

Android 12系统源码_WindowInsets (一)WindowInsets相关类和功能介绍

一、什么是WindowInsets? WindowInsets源码解释为Window Content的一系列插值集合,可以理解为可以将其理解为不同的窗口装饰区域类型,比如一个Activity相对于手机屏幕需要空出的地方以腾给StatusBar、Ime、NavigationBar等系统窗口,具体表现为该区域需要的上下左右的宽高。…

Oracle Linux 8.8 发布 - Oracle 提供支持 RHEL 兼容发行版

Oracle Linux 8.8 发布 - Oracle 提供支持 RHEL 兼容发行版 Oracle Linux with Unbreakable Enterprise Kernel (UEK) & Red Hat compatible kernel (RHCK) 请访问原文链接&#xff1a;https://sysin.org/blog/oracle-linux-8/&#xff0c;查看最新版。原创作品&#xff…

opencv实践项目-图像卡通化

目录 1.如何使图像卡通画2.铅笔素描滤波器3. 细节增强滤波器4. 双边过滤器5. 铅笔边缘滤波器 1.如何使图像卡通画 我们通常需要执行两个主要步骤将图像转换为卡通图像&#xff1a;边缘检测和区域平滑。 边缘检测的主要目的显然是为了强调图像的边缘&#xff0c;因为卡通图像通…

银行从业——法律法规

第一章、经济基础知识 第一节、宏观经济分析 【 知识点1】 宏观经济发展目标 宏观经济发展的总体目标一般包括四个&#xff1a; 宏观经济发展的总体目标 衡量指标1、经济增长国内生产总值&#xff08;GDP&#xff09;2、充分就业 失业率3、物价稳定通货膨胀率4、国际…

Sangria:类似Nova folding scheme的relaxed PLONK for PLONK

1. 引言 前序博客有&#xff1a; Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记SuperNova&#xff1a;为多指令虚拟机执行提供递归证明基于Nova/SuperNova的zkVMSangria&#xff1a;PLONK Folding 主要见2023年4月 Geometry团队Nicolas Mohnblat…

多线程编程(1)

本篇重点 了解进程和线程的区别和联系使用Java&#xff0c;实现创建线程的五种写法 目录 使用Java进行多线程编程方法一&#xff1a;继承 Thread, 重写 run方法二: 实现 Runnable, 重写 run方法三: 继承 Thread, 重写 run, 使用匿名内部类方法四: 实现 Runnable, 重写 run, 使用…

【剑指offer】数据结构——数

目录 数据结构——数直接解【剑指offer】43. 1&#xff5e;n 整数中 1 出现的次数【剑指offer】44. 数字序列中某一位的数字【剑指offer】49. 丑数【剑指offer】60. n个骰子的点数【剑指offer】62. 圆圈中最后剩下的数字【剑指offer】64. 求12…n 特殊解——分治法 &#xff1a…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - 划分子网、构造超网

目录 一、概述二、分类IP地址不合理的地方三、划分子网四、无分类编址方法 一、概述 前面的文章介绍了网络层的网际协议IP&#xff0c;介绍了IP地址的定义&#xff0c;知道了IP地址分为网络标识(网络地址)、主机标识(主机地址)两部分&#xff0c;也清楚了最初IP地址是按照分类被…

C++ 迷宫问题

文章目录 题目描述输入描述输出描述示例1 答案&#xff1a;代码讲解&#xff1a; 题目描述 在一个给定大小的迷宫中&#xff0c;有一个起点和一个终点&#xff0c;中间夹杂着一些墙壁 如果能走到终点输出 YES 否则输出 NO 输入描述 迷宫的大小 nm&#xff0c;其中 n 表示行数…

[数据结构 -- C语言] 堆(Heap),你小子就是堆,看我如何透彻的将你拿捏

目录 1、堆的概念及结构 1.1 概念&#xff08;概念总是重要的&#xff09; 1.2 结构&#xff0c;分为两种 1.2.1 小堆/小根堆示例 1.2.2 大堆/大根堆示例 2、堆的接口 3、接口实现 3.1 堆的初始化 3.2 堆的销毁 3.3 堆的插入 功能分析&#xff1a; 功能实现&#x…

zabbix动作执行失败 No media defined for user.

问题 zabbix动作执行失败 No media defined for user. 详细问题 解决方案 1&#xff08;导航栏&#xff09;用户 → \rightarrow →报警媒介 → \rightarrow →添加 2 选择类型 → \rightarrow →收件人 → \rightarrow →添加 3 更新 解决原因 笔者由于未点击更新钮导…

【计算机网络】3、IO 多路复用:select、poll、epoll、reactor | 阻塞非阻塞、同步异步

文章目录 一、select()1.1 用法1.1 实战 二、poll()2.1 用法2.2 实战 三、阻塞、非阻塞3.1 非阻塞 IO3.1.1 read()3.1.2 write()3.1.3 accept()3.1.4 connect()3.1.5 非阻塞IO select() 多路复用实战 四、epoll()4.1 epoll_create()4.2 epoll_ctl()4.3 epoll_wait()4.4 实战4.…

GAN在图像转译领域的应用-CycleGANPix2Pix

在之前的博客中向大家介绍了生成对抗网络GAN的相关概念以及条件GAN&#xff0c;DCGAN相关内容&#xff0c;需要的小伙伴可以点击以下链接了解~生成对抗网络GAN_生成对抗网络流程_春末的南方城市的博客-CSDN博客生成对抗网络Generative Adversarial Networks&#xff08;GAN&…

使用Docker安装Guacamole远程网关并配置录像回放

一、参考 guacamole配置guacamole使用Docker安装guacamole在浏览器中播放录像guacamole插件下载 二、环境 操作系统&#xff1a;Anolis OS 8.6 QU1 docker版本&#xff1a;23.0.5 docker compose版本&#xff1a;v2.17.3 docker-image-guacamole&#xff1a;1.5.1 docker-image…

线段树C++详细讲解和个人见解

问题引入 假设有这样的问题&#xff1a;有n个数&#xff0c;m次操作&#xff0c;操作分为&#xff1a;修改某一个数或者查询一段区间的值 数据范围是&#xff08;1 < n, m<1e9)。 这种题大家一看就知道打暴力&#xff0c;但是一看数据范围就知道只能得部分。 我们之前…