小朋友你是否有很多问号,python 中有时候参数中带了 * 和 ** 是怎么回事? 如果是不懂或者挑刺,欢迎来阅读本篇

Image

先说最简单的序列传参 #

def calc(a, b, c):
    return (a + b) * c

print(calc(1, 5, 10))
l = [1, 5, 10]
print(calc(*l))

* 将 l 列表/元组等序列依次输入 而 ** 将字典依次传入

def health_check(name, age, height, weight):
    print(name)
    print("您的健康状态良好")

# 注意键一定要是字符串,key 要与参数一样
param = {"name": "张三", "age": 32, "height": 178, "weight": 85.5}
health_check(name="张三", age=32, height=178, weight=85.5)
health_check(**param)

从效果上可以看成,**字典 其实是转换成 形参=值 传入

但是我们经常会看到这样的参数 *args, **kwargs,其实作用就是上面最简单的作用,而 **kwargs 也要格外注意!

`*args`, `**kwargs` #

def test(name, age, *args, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)
    print("name:", name)
    print("age", age)
    for i in args:
        print("args:", i)
    for k, v in kwargs.items():
        print("kwargs:", k, v)

if __name__ == '__main__':
    test('yang', 18, 'is', 'a', 'good', 'man', sex="male")

result:

args: ('is', 'a', 'good', 'man')
kwargs: {'sex': 'male'}
name: yang
age 18
args: is
args: a
args: good
args: man
kwargs: sex male

我们看到,调用函数 test 的参数为 'yang', 18, 'is', 'a', 'good', 'man', sex="male",python 依次将 yang 和 18 赋给了 name 和 age,将 'is', 'a', 'good', 'man' 赋给了 args 元组,将 sex="male" 转换成了一个字典给了 kwargs。

你可以认为它们收了多余的参数,并且重新转元组和字典。

那好,**kwargs 就会把字典转成形参=...的样子(这在前面提到了),直接输出 kwargs 得到的是字典。

于是这就诞生了一个问题 #

为什么 print(*args) 可以输出 而 print(**kwargs) 却报错 'name' is an invalid keyword argument for this function

正是因为 **kwargs 相当于 形参=...,于是相当于传入 print(形参=...),可是 print 没有这个形参啊,因此报错。

所以可以说 **kwargs就是形参=值<===>形参=值 转换字典存于 kwargs 这个变量中 **\*args 就是值 <===> 值转成元组存 args 这个变量中**

继续,我们往往还在装饰器中看到 `*args` 与 `**kwargs` #

def log(name=None):
    def decorator(func):
        """如果函数func是带参数,那么就得加入魔法参数,把函数的参数传入"""
        """*args传的是元组,**kwargs传的是字典"""
        def wrapper(*args, **kwargs):
            print('{0}.start'.format(name))
            # print(args)
            # print(kwargs)
            rest = func(*args, **kwargs)
            print('{0}.end'.format(name))
            """ func 有返回值,也要返回"""
            return rest
        return wrapper
    return decorator

@log("from add")
def add(a, b):
    return a + b

通过 *args**kwargs 传值,而 args 和 kwargs 就跟上面存的一样,自行组合成元组或者字典,并且通过 *** 转换(不再重复)

def test(name, age, *args, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)
    print("name:", name)
    print('age:', age)

if __name__ == '__main__':
    d = {"name1": "杨彦星", 'age1': 18}
    l = ['a', 'b', 'c']
    t = ('d', 'e', 'f')
    test(*l, *t, **d)  # 看参数位置

输出:

args: ('c', 'd', 'e', 'f')
kwargs: {'age1': 18, 'name1': '杨彦星'}
name: a
age: b

这里有几个问题需要注意下,由于 **kwargs 只能定义在函数参数的最后,它后面不能再有形参了,但是 * 参数可以不限制参数的位置,所以上面的代码在定义 d 的时候不能再有 name 和 age 的定义了,因为在传参的时候,已经将 'a' 赋给了 name, 'b' 赋给了 age,所以如果之后再有 name 的定义则会报参数重复定义的错误。但是却可以如下的定义:

def test(*args, name, age, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)
    print("name:", name)
    print('age:', age)

if __name__ == '__main__':
    d = {"name": "杨彦星", 'age': 18, 'sex': 'male'}
    l = ['a', 'b', 'c']
    t = ('d', 'e', 'f')
    test(*l, *t, **d)

因为把 *args 放到了第一个参数,所以在调用函数时为 test('a','b','c','d','e','f',name="杨彦星",age=18,sex='male'),此时如果 d 的定义的时候没有 name 和 age 也会报错。

好吧,也就是说写到这里,*args 也有重要点,它表示值,如果没有像 ** 字典,就会把值代入默认的形参中