前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >说说 Python 装饰器「参数」的那些事儿。

说说 Python 装饰器「参数」的那些事儿。

作者头像
编程文青李狗蛋
发布2019-06-19 17:14:54
4730
发布2019-06-19 17:14:54
举报
文章被收录于专栏:编程文青李狗蛋
00.获取函数参数

在上一篇的最后,我写了一个装饰器的例子。为了方便,我把它拿过来给你看:

代码语言:javascript
复制
def check_admin(func):
    def wrapper(*args, **kwargs):
        if kwargs.get('username') != 'admin':
            raise Exception('This user do not have permission')
        return func(*args, **kwargs)
    return wrapper

class Stack:
    def __init__(self):
        self.item = []

    @check_admin
    def push(self,username,item):
        self.item.append(item)

    @check_admin
    def pop(self,username):
        if not self.item:
            raise Exception('NO elem in stack')
        return self.item.pop()

接下来我们来使用一下上述定义的 Stack 类,在用的过程中我发现了一件很奇怪的事,当我向下面这样调用的时候程序是正常的:

代码语言:javascript
复制
s = Stack()
s.push(username='admin', item=1)

而当我像下面这样调用的时候,程序就会报错:

代码语言:javascript
复制
s = Stack()
s.push('admin', item=1)

你可能会生成“这怎么会出错”的疑问,因为按照我们理解的,「关键字参数」会根据名字进行匹配,而「位置参数」会根据参数所在的顺序进行匹配,既然这样的话,admin 明明是穿进了 username 变量,那为啥会出错呢?

其实上面我们怀疑的原因都没有问题,有问题的是我们的装饰器写的有问题。问题就是出现在装饰器的参数传递上

在 check_admin 这个装饰器中,我直接从 kwargs.get 中获得了 username 这个值。第一个正确是因为我用的是关键字参数传递的 username,那么 username 的变量以及值理应在 kwargs 中,第二个错误是因为我们用位置参数传递的 username,那么 username 的值出现在 args 中。

那么新的问题来了,作为用户来讲,无论使用位置参数或者是关键字参数都是对的,这个我们是无法去控制的,那这个问题应该怎么解决呢?Python 说:“用 inspect 模块”。其中的 getcallargs 可以解决这个问题,它返回的是一个字典,这个字典里保存了函数的所有参数。加下来我们看该怎么改:

代码语言:javascript
复制
import inspect

def check_admin(func):
    def wrapper(*args, **kwargs):
        func_args = inspect.getcallargs(func, *args, **kwargs)
        if func_args.get('username') != 'admin':
            raise Exception('This user do not have permission')
        return func(*args, **kwargs)
    return wrapper

01.带参数的装饰器

在我们之前熟知的装饰器语法中,外层函数的参数是被装饰的函数,内层函数的参数是被装饰的函数的参数。但是有些时候我们想针对不同的函数装饰器有些变化怎么办,即给装饰器后面带上相应的参数。

比如有个针对加和减的装饰器如下所示:

代码语言:javascript
复制
s = Stack()
s.push('admin', item=1)

def tips(func):
    def wrapper(*args):
        print('start')
        func(*args)
        print('end')
    return wrapper

@tips
def add(a,b):
    print(a+b)
    
@tips
def sub(a,b):
    print(a-b)

但这个时候我想做点改变,想把函数的名字也给打出来,这个时候我们装饰器肯定就是要带上参数了,参数传的就是函数的名字,这个时候我们该怎么办?其实也简单的很,那就再嵌套一层函数呗。具体如下所示:

代码语言:javascript
复制
def new_tips(argv):
    def tips(func):
        def wrapper(*args):
            print('start %s' %(argv))
            func(*args)
            print('end')

        return wrapper
    return tips

@new_tips('add')
def add(a, b):
    print(a + b)

@new_tips('sub')
def sub(a, b):
    print(a - b)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python空间 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档