Python装饰器的入门

117 阅读4分钟

Python装饰器是什么?

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

入门装饰器之前要了解的几个知识

1. 函数可以作为参数进行传递

def func():
    print('我是函数')

def ggg(fn):
    fn()

ggg(func) # 这里的fnc就作为了实参进行传递 输出答案:  我是函数

2.函数可以作为返回值进行返回

def func():
    def inner():
        print("123")
    return inner  #将inner的函数地址返回
ret = func() #将inner的函数地址赋值给ret
ret()  #调用ret函数也就是调用inner函数

3.函数名称可以当成变量一样进行赋值操作

def func1():
    print("我是函数1")
def func2():
    print("我是函数2")

func1 = func2  #将func2的地址赋值给func1
func1() # 我是函数2

我们对装饰器进行推导

比如说我们想打游戏,我想要打dnf或者lol,就可以定义两个函数。

def play_dnf():
    print("你好啊,我是哥布林")

def play_lol():
    print("德玛西亚")

为了升级体验,我想要开挂,那我们可以这个样子。我们雇一个管家来帮我们开挂。

def play_dnf():
    print("你好啊,我是哥布林")

def play_lol():
    print("德玛西亚")

def guanjia(game):
    print("打开外挂")
    game()
    print("关闭外挂")

guanjia(play_lol) 

这样子好像就Ok了,但是我们有没有发现一个问题,现在这游戏就不是我们在玩了,变成管家在帮我们玩了,那我怎么样让我能玩到这个游戏呢?可以这样。

def play_dnf():
    print("你好啊,我是哥布林")

def play_lol():
    print("德玛西亚")

def guanjia(game):
    def inner():
        print("打开外挂")
        game()
        print("关闭外挂")
    return inner

play_lol = guanjia(play_lol) #让管家把游戏重新封装一遍,我这波把原来的游戏替换了
play_lol()

我们调整一下函数的顺序,将程序改成如下文代码这样。这就是装饰器了,它与上一段代码的效果是一样的。

def guanjia(game):
    def inner():
        print("打开外挂")
        game()
        print("关闭外挂")
    return inner

@guanjia
def play_dnf():
    print("你好啊,我是哥布林")

@guanjia
def play_lol():
    print("德玛西亚")

play_lol() #play_lol = guanjia(play_lol); play_lol()

装饰器传参的问题

这里我们要了解两个知识点。

  • *是位置参数的动态传参,用于表示不能确定个数的参数。
  • ** 表示关键字的动态传参(它的形式是字典)。
  • 这里的arg,kwarg只是习惯使用的形参名,可以使用其他合理形参替换。

这里的参数其实是传给内层函数inner的

def guanjia(game):
    def inner(*args,**kwargs):
        print(*args) #这里的*是展开运算符的意思,类似JS的*   admin 123456
        game()
        print(kwargs) #{'gender': '男'}

    return inner

@guanjia
def play_dnf():
    print("你好啊,我是哥布林")

@guanjia
def play_lol():
    print("德玛西亚")

play_lol("admin","123456",gender = "男")

装饰器的返回值

如果被装饰的函数要有返回值我们得怎么操作呢?

def guanjia(game):
    def inner(*args,**kwargs):
        print("打开外挂")
        ret = game()
        print("关闭外挂")
        return ret
    return inner

@guanjia
def play_dnf():
    print("你好啊,我是哥布林")


@guanjia
def play_lol():
    print("德玛西亚")
    return "金色的装备"

ret = play_lol("admin","123456") 
print(ret)

输出结果

打开外挂
德玛西亚
关闭外挂
金色的装备

如果要传参

def guanjia(game):
    def inner(*args,**kwargs):
        print("打开外挂")
        ret = game(*args,**kwargs)
        print("关闭外挂")
        return ret
    return inner

@guanjia
def play_dnf():
    print("你好啊,我是哥布林")


@guanjia
def play_lol(username,password):
    print(f"你的用户名是{username},你的密码是{password}")
    return "金色的装备"

ret = play_lol("admin","123456")
print(ret)

装饰器的通用写法

"""
装饰器: -->要求记住最后的结论
    装饰器本质上是一个闭包
    作用:
        在不改变原有函数调用的情况下,给函数增加新的功能,
        直白:可以在函数前后添加新功能,但不更改源代码
    在用户登录的地方,日志,..
    通用装饰器的写法:
        def wrapper(fn): # wrapper:装饰器,fn:目标函数
            def inner(*args,**kwargs):
                #在目标函数执行之前....
                ret = fn(*args,**kwargs)
                #在目标函数执行之后....
                return ret
            return inner  千万别加()
        
        @wrapper
        def target():
            pass
        
        target() # => inner()
"""

多个装饰器的运行结果

def wrapper1(fn):
    def inner(*args,**kwargs):
        print("这里是wrapper1进入")
        ret = fn(*args,**kwargs)
        print("这里是wrapper1退出")
        return ret
    return inner

def wrapper2(fn):
    def inner(*args,**kwargs):
        print("这里是wrapper2进入")
        ret = fn(*args,**kwargs)
        print("这里是wrapper2退出")
        return ret
    return inner

@wrapper1  # target = wrapper1(wrapper2.inner) => target:wrapper1.inner
@wrapper2  # target = wrapper2(target) => target:wrapper2.inner
def target():
    print("我是目标")

target()

运行结果

这里是wrapper1进入
这里是wrapper2进入
我是目标
这里是wrapper2退出
这里是wrapper1退出

装饰器的实战小demo


login_flag = False
def loginVerify(target):
    def inner(*args,**kwargs):
        global login_flag
        if login_flag == False:
            while 1:
                print("还未完成登录操作")
                username = input(">>>>")
                password = input(">>>>")
                if(username == 'admin' and password == '123456'):
                    print("登录成功")
                    login_flag = True
                    break
                else:
                    print("登录失败,用户名或者密码错误")
        ret = target(*args,**kwargs)
        return ret
    return inner

@loginVerify
def insert():
    print("增加数据")

@loginVerify
def delete():
    print("删除数据")

@loginVerify
def update():
    print("更新数据")

@loginVerify
def search():
    print("搜索数据")

insert()
delete()
update()
search()