python函数之有参装饰器

一、为什么要有有参装饰器?

来看之前的无参装饰器

# 无参装饰器
def outter(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)  # 我们需要一个变量接受函数的返回值
        end = time.time()
        print("run time is %s" % (end - start))
        return res   # 我们装饰器的核心是wrapper函数,只需要在wrapper函数中提供返回值即可
    return wrapper

@outter # @+装饰器名称,可以快速完成 pint1 = outter(print1)的函数赋值
def print1(name):
    print(name)
    time.sleep(1)
    return 111

可以看到,我们从outter()和wrapper()向内层函数进行了传参,outter函数传入的是我们所要修饰的函数对象,wrapper则是将参数原封不动的传给了修饰的函数对象,如果现在有个新的需求:

  • 设计一个装饰器,为函数增加认证功能,其中登陆的认证来源有多个(file、mysql)

首先看一下无参装饰器能否实现:

# 认证功能装饰器
def outter(func):
    def wrapper(*args,**kwargs):
        name = input("请输入用户名>>>")
        pwd = input("请输入密码>>>")
        if mode == "1":     # 需要通过mode进行判断信息来源,但是没有参数传入
            if name == "zhangda" and pwd == "123":
                print("登陆成功!")
                res1 = func(*args,**kwargs)
                return res1
            else:
                print("用户名或者密码错误")
        elif mode == "2":   #需要通过mode进行判断信息来源,但是没有参数传入
         print("认证来源:mysql")
     else:
         print("未知来源")
return wrapper

我们需要传入一个mode参数进行判断,但是wrapper()需要给func()原样传入参数,所以不能添加mode,外层的outter()可以为mode传参,但是由于python语法糖限制,里面除了func外,不能添加其他的参数,那这个mode参数怎么传入呢?

我们可以在最外层函数进行传入

# 认证功能装饰器
def outter(func):
    mode = "file"
    def wrapper(*args,**kwargs):
        name = input("请输入用户名>>>")
        pwd = input("请输入密码>>>")
        if mode == "1":
            if name == "zhangda" and pwd == "123":
                print("登陆成功!")
                res1 = func(*args,**kwargs)
                return res1
            else:
                print("用户名或者密码错误")
        elif mode == "2":
            print("认证来源:mysql")
        else:
            print("未知来源")
    return wrapper

@outter
def f1():
    print("hhhhh")

但是这样每次使用装饰器都需要修改,并且不能同时满足多个信息来源需求,回想之前给函数传参的两种方法,既然直接传参行不通,我们想到了利用闭包函数特性,再包一层函数,这就是有参装饰器

def outter2(mode="file"):
    def outter(func):
        def wrapper(*args,**kwargs):
            name = input("Username:").strip()
            pwd = input("Password:").strip()

            if mode == "file":
                if name == "egon" and pwd == "123":
                    print("登陆成功!")
                    res = func(*args,**kwargs)
                    return res
                else:
                    print("登陆失败!")
            elif mode == "mysql":
                print("基于mysql认证!")
            else:
                print("未知来源认证!")
        return wrapper
    return outter


@outter2("mysql")
def index(name):
    print("{}".format(name))

index("hhhhhh")

在这里将之前的装饰器outter看做一个整体,对他进行装饰,并且最外层函数可以传入任意参数的,有参装饰的语法糖:@函数名(参数1,参数2......)

二、装饰器wraps

我们知道,装饰器其实就是定义了一个闭包函数,包含了原来的函数与功能并且添加新功能,最后通过函数对象赋值实现了全局调用,但是函数内存地址已经改变,只是名称看着一样

def outter(func):
    def wrapper(*args,**kwargs):
        """wrapper注释"""
        print("11111")
        res = func(*args,**kwargs)
        return res
    return wrapper

@outter
def index():
    """index注释"""
    print("2222")

print(index.__name__) # 打印函数的名称
print(index.__doc__) # 打印函数的注释
print(index) # 打印函数内存地址

运行结果如下:

 

 index只是披着index外衣的wrapper函数,他的本质其实是wrapper,这不会影响函数的原始使用,但是如果有需求,希望这些属性看着不变呢?

def outter(func):
    def wrapper(*args,**kwargs):
        """wrapper注释"""
        print("11111")
        res = func(*args,**kwargs)
        return res
    wrapper.__name__ = func.__name__
    wrapper.__doc__ = func.__doc__
    return wrapper

@outter
def index():
    """index注释"""
    print("2222")

print(index.__name__) # 打印函数的名称
print(index.__doc__) # 打印函数的注释
print(index) # 打印函数内存地址

我们可以通过将wrapper的名称等信息重新复制,将原函数的信息赋值给wrapper即可:

 

 其实python内置了一个库functools,提供了装饰器实现这一功能,用法如下:

from functools import wraps

def outter(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        """wrapper注释"""
        print("11111")
        res = func(*args,**kwargs)
        return res
    # wrapper.__name__ = func.__name__
    # wrapper.__doc__ = func.__doc__
    return wrapper

@outter
def index():
    """index注释"""
    print("2222")

print(index.__name__) # 打印函数的名称
print(index.__doc__) # 打印函数的注释
print(index) # 打印函数内存地址

 

 

三、总结

装饰器模板

  • 无参装饰器
def outter(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper
  • 有参修饰器
def outter2(x):
    def outter(func):
        def wrapper(*args,**kwargs):
            print(x)
            res = func(*args,**kwargs)
            return res
        return wrapper
    return outter

@outter2("1")
def f1():
    print("2")

 

posted @ 2021-03-30 16:11  猫咪也会码代码  阅读(149)  评论(0)    收藏  举报