*args and **kwargs

自定义的装饰器,由于硬编码的原因只能应用在一类具体的方法上,这类方法接受两个参数,传递给闭包捕获的函数,如果我们实现一个能够应用在任何方法上的装饰器要怎么做呢?在比如,如果过我们要实现一个能够应用到任何方法上的计数器的装饰器,不需要改变原有的方法的任何逻辑,这意味着装饰器能够接受任何签名的函数作为自己被装饰的方法,同时能够传递给它的参数对被装饰的方法进行调用。

非常巧合的是python正好有支持这个特性的语法,可以阅读 Python Tutoral 获取更多的细节,当定义函数的时候使用了 *,意味着这些通过位置传递的参数将会带有 * 亲前缀的变量中,所以:

def one(*aegs):
    print( args) # 1

one()

one(1,2,3)
(1,2,3)

def two(x, y, *args):
    print(x, y, args)

two('s', 'b', 'c')
a b ('c',)

第一个函数 one 只是简单的将任何传递的位置参数全部打印出来而已,你们能够看到,在代码 # 1 处我们只是引用了函数内的变量 args,* args仅仅只是用在函数定义的是时候用来表示位置参数应该存储的变量 wrgs 里面, Python 允许我们制造一些参数并且通过args捕获其他剩余的未被捕捉的位置参数,就行 # 2 处所示的那样。

*操作符在函数被调用时候也能使用,意义基本是一样的,当调用一个函数的时候,一个用 * 标志的变量意思是变量里面的内容被提取出来然后当作位置参数被使用。

同样的,我们来举个例子:

def add(x, y):
    return x + y

lst = [1,2]
add(lst[0], lst[1]) # 1
3

add(*lst)
3

# 1处和 # 2处 的代码所做的事情其实是一样的 , 在 # 2 处,python 为我们所作的事其实也可以手动跑迷宫完成,这也不是什么坏事,*args 要么事表示调用方法打的时候额外的参数可以凑够一个可迭代的列表中取得,要么就是定义方法的时候标志这个方法能够接受任意的位置参数。

接下来提到的 ** 会稍微复杂一点,**代表着键值对的字典,和* 锁代表的意义相差无几,也很简单对不对:

 

def foo(**kwargs):
    print(kwargs)

foo()
()

foo(x= 1, 7)
['y':2, 'x':1 ]

当我们定义一个函数的时候,我们能能够用 ** kwargs 表明,所有未被捕获的关键字参数都应该存在在 kwargs 的字典中。如前所讲, args 和 kwargs 并不是 python 语法的一部分,但在定义函数的时候,使用这样的变量算是不成文的约定,和 * 一样,我们同样可以在定义或者调用函数的使用 **:

dct = {'a': 1, "b": 2}
def bar(x, y):
    return x + y
babr(**dct)
3

更通用的装饰器:

有了这招新的技能之后,我们虽败吗就可以写一个能够记录下传递给函数的装饰器了,先来个简单的把日志输出到界面的例子:

def logger(func):
    def inner(*args, kwargs): # 1
        print("Arguments were: %s, %s"% (aargs, kwargs))
        return func(*args, **kwargs) # 2

     return inner

请注意我们的函数inner,它能够接受任何数量和类型的参数并把它传递给白包装的方法,这让我们能够用这个装饰器来装饰任何的函数:

@logger
def foo(x, y=1):
    return x + y

@ logger 
def foo2():
    return 2

foo1(5, 4)
Arguments were: (5, 4)
foo1(1)
Arguments were: (1,)

随便调用那个我们定义的方法,相应的日志也会打印到输出窗口, 和我们预期的一样。

原文地址:https://www.cnblogs.com/jcjc/p/11445915.html