有参装饰器---及wraps修饰边边角角

python学习18——有参装饰器及wraps

 

了解无参装饰器的实现原理后,我们可以再实现一个用来为被装饰对象添加认证功能的装饰器,实现的基本形式如下

def deco(func):
    def wrapper(*args,**kwargs):
        编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res
    return wrapper

 

如果我们想提供多种不同的认证方式以供选择,单从wrapper函数的实现角度改写如下

 
def deco(func):
        def wrapper(*args,**kwargs):
            if driver == 'file':
                编写基于文件的认证,认证通过则执行res=func(*args,**kwargs),并返回res
            elif driver == 'mysql':
                编写基于mysql认证,认证通过则执行res=func(*args,**kwargs),并返回res
        return wrapper
 
 

 

函数wrapper需要一个driver参数,而函数deco与wrapper的参数都有其特定的功能,不能用来接受其他类别的参数,可以在deco的外部再包一层函数auth,用来专门接受额外的参数,这样便保证了在auth函数内无论多少层都可以引用到

def auth(driver):
    def deco(func):
        ……
    return deco

 

此时我们就实现了一个有参装饰器,使用方式如下

 
先调用auth_type(driver='file'),得到@deco,deco是一个闭包函数,
包含了对外部作用域名字driver的引用,@deco的语法意义与无参装饰器一样
@auth(driver='file') 
def index():     
    pass
@auth(driver='mysql') 
def home():
    pass  
 

 

可以使用help(函数名)来查看函数的文档注释,本质就是查看函数的doc属性,但对于被装饰之后的函数,查看文档注释
 
@timer
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 wrapper in module __main__:

wrapper(*args, **kwargs)

None
 

 

在被装饰之后home=wrapper,查看home.name也可以发现home的函数名确实是wrapper,想要保留原函数的文档和函数名属性,需要修正装饰器

 
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    wrapper.__doc__=func.__doc__
    wrapper.__name__=func.__name__
    return wrapper
 

 

按照上述方式来实现保留原函数属性过于麻烦,functools模块下提供一个装饰器wraps专门用来帮我们实现这件事,用法如下

 
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper
 

 

 
由于语法糖@的限制,outter函数只能有一个参数,并且该才是只用来接收
被装饰对象的内存地址
 
def outter(func):
    # func = 函数的内存地址
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

# @outter # index=outter(index) # index=>wrapper
@outter # outter(index)
def index(x,y):
    print(x,y)
 

 


偷梁换柱之后
index的参数什么样子,wrapper的参数就应该什么样子
index的返回值什么样子,wrapper的返回值就应该什么样子
index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps


山炮玩法:
def auth(func,db_type):
def wrapper(*args, **kwargs):
name=input('your name>>>: ').strip()
pwd=input('your password>>>: ').strip()

if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')

return wrapper

# @auth # 账号密码的来源是文件
def index(x,y):
print('index->>%s:%s' %(x,y))

# @auth # 账号密码的来源是数据库
def home(name):
print('home->>%s' %name)

# @auth # 账号密码的来源是ldap
def transfer():
print('transfer')


index=auth(index,'file')
home=auth(home,'mysql')
transfer=auth(transfer,'ldap')

# index(1,2)
# home('egon')
# transfer()


山炮二
def auth(db_type):
def deco(func):
def wrapper(*args, **kwargs):
name=input('your name>>>: ').strip()
pwd=input('your password>>>: ').strip()

if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')

return wrapper
return deco

deco=auth(db_type='file')
@deco # 账号密码的来源是文件
def index(x,y):
print('index->>%s:%s' %(x,y))

deco=auth(db_type='mysql')
@deco # 账号密码的来源是数据库
def home(name):
print('home->>%s' %name)

deco=auth(db_type='ldap')
@deco # 账号密码的来源是ldap
def transfer():
print('transfer')


index(1,2)
home('egon')
transfer()


# 语法糖
def auth(db_type):
def deco(func):
def wrapper(*args, **kwargs):
name = input('your name>>>: ').strip()
pwd = input('your password>>>: ').strip()

if db_type == 'file':
print('基于文件的验证')
if name == 'egon' and pwd == '123':
res = func(*args, **kwargs) # index(1,2)
return res
else:
print('user or password error')
elif db_type == 'mysql':
print('基于mysql的验证')
elif db_type == 'ldap':
print('基于ldap的验证')
else:
print('不支持该db_type')
return wrapper
return deco


@auth(db_type='file') # @deco # index=deco(index) # index=wrapper
def index(x, y):
print('index->>%s:%s' % (x, y))

@auth(db_type='mysql') # @deco # home=deco(home) # home=wrapper
def home(name):
print('home->>%s' % name)


@auth(db_type='ldap') # 账号密码的来源是ldap
def transfer():
print('transfer')

# index(1, 2)
# home('egon')
# transfer()



 
# 有参装饰器模板
def 有参装饰器(x,y,z):
    def outter(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
    return outter

@有参装饰器(1,y=2,z=3)
def 被装饰对象():
    pass

 

posted @ 2021-03-16 19:20  欧阳锦涛  阅读(46)  评论(0)    收藏  举报
TOP 底部