python之装饰器的定义与使用(函数对象、闭包函数)(11)

前言

本篇文章主要是对python中一种特殊方法装饰器的讲解,通过前面的知识铺垫逐步了解装饰器的使用方法和原理;基础薄弱的朋友理解起来可能有困难,但不用担心,经过反复的练习,相信大家一定可以掌握,为了更好的学习本篇内容,推荐大家先去学习下面这篇文章

python之初识函数https://blog.csdn.net/m0_59470317/article/details/140453498

1、函数对象

函数对象是指函数可以被当做数据来使用,像变量一样被引用;其特点如下:

  1. 函数可以被引用
a = 1 
b = a # 将变量a 绑定给 变量b
print(b) # 结果:1

def a():
    return 666

b = a # 将函数a 绑定给 变量b,得到其内存地址
print(b) # 结果:<function a at 0x000002168F2F81F0>

b = a() # 将调用后的函数a 绑定给 变量b,得到函数a的返回值
print(b) # 结果:666

说明:
从上面可以看到一个变量可以被当做数据一样被其它变量使用,同理函数也可以被当做数据被其他变量引用,注意,若只绑定 函数名得到的是该函数的 内存地址,绑定函数名()才能运行函数,并得到函数的返回值

  1. 函数可以作为容器类型数据中的元素;如:列表、字典、元组
def a():
    return 666

b = [1,2,a] 
print(b) # 结果:[1, 2, <function a at 0x000001BF864C81F0>]

b = [1,2,a()] 
print(b) # 结果:[1, 2, 666]

说明:
从上面可以看到,函数可以作为列表中的元素被使用,若只有函数名得到其内存地址,而函数名()得到其返回值

  1. 函数可作为一个参数传入另外一个函数
def a(x,y):
    return x+y

def b(test):
    print(test)

b(a) # 将函数a 传入 函数b,得到其内存地址
b(a(1,2)) # 将调用后的函数a 传入 函数b,得到其返回值回值

输出结果:

<function a at 0x000001FC106881F0>
3
  1. 函数的返回值可以是一个函数
def a():
    print(666)

def b():
    return a # 将函数a 作为 函数b 的返回值,得到其内存地址
print(b()) # 结果:<function a at 0x0000013A27E881F0>

def c():
    return a() # 将调用的函数a 作为 函数c 的返回值,运行函数并得到其返回值
print(c()) # 结果: 666  None

2、闭包函数

一种以特殊形式构成的函数,实际上就是函数嵌套的一种,作用不大,主要是为了引导接下来要学习的装饰器,大家理解其含义即可;格式如下:

def 函数1(参数):
    def 函数2():
        代码
    return 函数2

闭包函数特点如下:

  1. 在全局作用域下可以使用局部作用域里面的函数
def a(): # 外层函数
    def b(): # 内层函数
        print(666)
    return b # 返回内层函数b 的内存地址

c = a() # 将函数a的返回值,绑定给变量c
c() # 通过变量c 直接调用函数b,输出结果:666

说明:
从上面可以看到,外层函数a 通过 return 直接返回 内层函数b 的内存地址,并绑定给变量c;这样我们就可以直接在全局作用域下,直接调用局部作用域中的函数b

  1. 得到一种新的为函数传参的方式
def a(x): 
    def b():
        print(x) # 内层函数b 可以不用传参,直接使用外层函数a 的参数 x
    return b

c = a(666) # 将 实参:666 传入函数a的 形参:x
c() # 输出结果:666

说明:
从上面可以看到,在全局作用域下为 外层函数a 传参,内层函数b 可以直接使用该参数,不用再次进行传参,更加方便

3、装饰器

3.1 引言

在日常开发程序中,我们通常会对已经开发好的程序进行更新,这时就需要更改程序的代码了;如:王者荣耀停服更新等等;而我们在更新代码的时候,一般会遵守一个原则;如下:

  • 开放封闭原则
    1、开放:指的是对程序中功能扩展的开放,也就是我们可以为程序增加新的功能
    2、封闭:指的是对程序代码的修改是封闭的,意思是我们可以为程序增加新的代码,但不能修原来的代码

  • 为什么要遵守这个原则?
    大家知道,我们日常使用的软件,其代码都是成千上万条的,里面的功能多而杂,若是随意更改,必然会出现问题;但现实中我们又经常会软件进行升级,为其增加一些功能或解决一些bug;所以肯定会新增一些代码,这时准遵守该原则,在不修改源代码的情况下,新增一些功能;可以避免程序出错;若想要直接修改源代码必须对该程序十分熟悉,有把握才行

  • 案例讲解:

1、错误示范:

# 程序中的一个功能:看电影
def movie(): 
    print('看电影')
movie() # 直接调用

# 遇到需求,需要更新功能,要求:账户登陆后才能看电影
if '账户登陆成功':
	movies() # 需要满足要求才能调用函数,源代码被修改

说明:
从上面可以看到,我们为程序中的功能添加新的功能时,更改了源代码,这样不符合开放封闭原则,那么如何才能在不更改源代码的情况下增加功能呢?我们往下看

2、正确示范:

# 程序中的一个功能:看电影
def movie(): 
    print('看电影')
movie() # 原函数

# 遇到需求,需要更新功能,要求:账户登陆后才能看电影
def new_movie(): # 定义一个新的函数用来扩展功能
	if '账户登陆成功':
		movies()
new_movie() # 新函数,在需要该功能时调用

说明:
从上面可以看到,为了瞒足开放封闭原则,我们重新定义了一个函数,在这个函数内对功能进行了升级,且源代码没有任何更改;这样在需要新增的功能时,就调用新函数;不要新增的功能时,调用原函数即可

  • 总结
    从上面的学习中,我们知道 若要对一个程序中的代码进行更新,为满足开放封闭原则,最好的解决方法就是,重新定义一个函数作为独立更新模块,这样就可以在不更改源代码的情况下,增加功能了;但当程序中有许多功能都需要进行更新时,按照这种方法,就要重新定义多个函数来更新代码;这样在开发大程序的时候,必定非常麻烦;因此python中推出了一种解决方法,那就是本篇文章的最终主角装饰器

3.2 装饰器的使用

这里先用理论说明一下装饰器的含义:
装饰器就是在满足开放封闭原则的情况下,通过特殊格式构造的一个可以增加程序中功能的函数;并且该函数可以通过 特殊方法 同时给程序中多个功能使用

  • 装饰器的语法格式
    装饰器的语法格式就是一种特殊的嵌套函数,与上面学习的闭包函数相似;我们通过将这种格式更新的代码写入函数,然后给程序中需要更新的功能使用;如下:

装饰器模板:

def 函数名1(形参名):
    def 函数名2(*args,**kwargs):
        变量名 = 形参名(*args,**kwargs)
        return 变量名
    return 函数名2
  • 装饰器原理讲解
# 装饰器
def a(func_name): # 外层函数,func_name为形参用于接收 程序中功能的名称
    def b(*args,**kwargs): # 内层函数,*args,**kwargs表示接受任意形式的参数
        data = func_name(*args,**kwargs) # *args,**kwargs表示将接受的参数炸开后传入函数
        return data # 将函数中的返回值传出去
    return b # 返回内层函数b 的内存地址

# 下面是一个程序中的几个功能,包含了所有情况
def movie(): # 功能1:看电影;没有参数和返回值的情况
    print('看电影')

def homework(): # 功能2:写作业;有返回值的情况
    return '写数学作业'

def game(x): # 功能3:玩游戏;有参数的情况
    print(f'我在玩{x}')

c = a(game) # 调用函数a,根据需要更新功能的函数,传入不同的函数名:movie、homework、game;这里假如需要更新功能3的代码,将函数名 game 传入;得到装饰器中内存函数b 的内存地址绑定给 变量c
c('王者') # 这里相当于直接调用装饰器中 内存函数b

说明:
从上面可以看到,我们可以通过定义一个装饰器,这样调用装饰器时可以将程序中不同类型的函数名通过参数放入装饰器中,在装饰器中的内层函数对这些函数的功能进行更新,最后通过返回的内层函数的内存地址,直接调用内存函数,从而达到程序功能的更新;当然这样还不是装饰器的完整形态,我们可以不用直接调用的方法来使用装饰器,而选择用python内置的方法来调用 装饰器,我们往下看

  • 装饰器的调用方法

语法格式:@装饰器名 ,在需要增加功能的函数上方使用
装饰器名也就是外层函数的名字

案例:

# 装饰器
def a(func_name): # 外层函数a
    def b(*args,**kwargs): # 在内层函数b中 对需要更新功能的函数进行代码的增加、扩展
        if '账号登录':
            print('登陆成功')
            data = func_name(*args,**kwargs)
        return data
    return b

# 下面是一个程序中的几个功能
# 在每个函数上加上装饰器后,调用函数得到的就是装饰器中内层函数执行后的结果
@a 
def movie(): # 功能1:看电影
    print('看电影')
@a
def homework(): # 功能2:写作业
    return '写数学作业'
@a
def game(x): # 功能3:玩游戏
    print(f'我在玩{x}')

movie() 
print(homework())
game('王者')

输出结果:

登陆成功
看电影
登陆成功
写数学作业
登陆成功
我在玩王者

说明:
从上面可以看到,我们定义好装饰器后,只用在需要更新的功能函数上方加上 @装饰器名;这样在调用该函数后,得到的结果就是装饰器中内层函数执行的结果;以后需要增加功能,直接在装饰器内扩展代码即可;若不需要增加的功能,将函数上方的 @装饰器名 删除或注释掉就可以了;这样既满足开放封闭原则,又可以同时给多个函数扩展新的功能;非常方便

3.3 总结

前面所学的所有内容都只是为了更好的理解装饰器的原理,可能会比较复杂,大家不需要钻牛角尖;只需要明白一点,装饰器是所有函数基础知识组合成的一个模板;用来给其它函数增加功能的,我们只需要记住如何使用该模板即可

模板示例:

# 装饰器
def a(func_name): # func_name 用于接收传入的函数名
    def b(*args,**kwargs): # 在内层函数b中 对需要更新功能的函数进行代码的增加、扩展
    	# 在这里写对下面函数进行功能扩展的代码
        data = func_name(*args,**kwargs) # 相当于调用传入的函数
        return data
    return b

# 下面是一个程序中的某个功能
@a  # 表示使用装饰器
def movie(): # 功能1:看电影
    print('看电影')
movie() # 调用后默认将函数名通过参数传给装饰器中内层函数使用,并得到内存函数执行的结果

案例演示:对程序中某个功能要求,账户登录后,才能使用

# 装饰器
def a(func_name):
    def b(*args,**kwargs):
        if '账号登录': # 增加功能,账户登录后才能使用下面的函数
            print('登陆成功')
            data = func_name(*args,**kwargs)
        return data
    return b

# 下面是一个程序中的某个功能
@a # 使用装饰器,为函数添加功能
def movie(): # 功能1:看电影
    print('看电影')
movie() # 得到增加功能后的结果

输出结果:

登陆成功
看电影
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盲敲代码的阿豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值