一、理论解释:
*参数与**参数是Python参数中的重点知识,他们都被称为可变参数(任意参数),我们经常会在代码中看到*args、**kwargs
作为函数定义时:收集未匹配参数组成tuple或dict对象
1、*参数收集所有未匹配的位置参数组成一个tuple对象,局部变量args指向此tuple对象
2、**参数收集所有未匹配的关键字参数组成一个dict对象,局部变量kwargs指向此dict对象
def temp(*args,**kwargs):
pass
作为函数调用时:解包功能
1、*参数用于解包tuple对象的每个元素,作为一个一个的位置参数传入到函数中
2、**参数用于解包dict对象的每个元素,作为一个一个的关键字参数传入到函数中
my_tuple = ("wang","yuan","wai")
temp(*my_tuple)
#---等同于---#
temp("wangyuan","yuan","wai")
my_dict = {"name":"wangyuanwai","age":32}
temp(**my_dict)
#----等同于----#
temp(name="wangyuanwai",age=32)
二、 *args 例子
这些基本概念暂时不理解很正常,完全理解需要一个过程……接下来的几个 *args 例子会说明这些概念,希望可以对每一位学习Python的同学带来帮助!!
1)包含两个位置参数的函数print_str
def print_str(first, second):
print(first)
print(second)
只传1个参数调用print_str()函数,会发生什么呢?
In [31]: print_str("hello")
TypeError: print_str() missing 1 required positional argument: 'second'
TypeError:解释器在控制台告知print_str()函数需要2个参数,而你只为print_str()函数传入了1个参数!
思考:怎么修改print_str()函数为即可接受一个参数、也可接受两个参数、甚者接受数量不定的更多参数呢?
2)修改print_str()函数可接受一个参数、也可接受数量不定的参数
将print_str()函数的最后一个参数修改为可变参数*second
def print_str(first, *second):
print(first)
print(second)
此时我们再传一个参数调用print_str()函数,看看这次发生什么?
In [34]: print_str("hello")
hello
()
这次不再报错,传入的第一个字符串参数"hello"打印出来了,没有传入参数的*second则打印的是一个tuple对象的字符串表示形式,即一个括号"()" 。 注意:()表示含有0个元素的tuple对象!
思考:为什么second变量变成一个tuple对象了?我们继续向下学习!
3)再做一个实验,为print_str()函数传入四个参数…会发生什么?
In [35]: print_str("hello","美女","小猫","青蛙")
hello
('美女', '小猫', '青蛙')
第一个参数“hello”,正常打印在第一行……
第二个参数"美女",第三个参数“小猫”,第四个参数“青蛙”在函数的内部被组装进1个新的tuple对象中,而这个新的tuple对象会赋值给变量second,此时局部变量second指向了一个tuple对象
说明:函数调用时传入的参数,会按照从左到右的顺序依次在函数中使用,最左侧的参数先由位置参数first使用(匹配),剩下的所有未匹配的参数会被自动收集到1个新的tuple对象中,而局部变量second会指向这个新的tuple对象
注意:*参数只收集未匹配的位置参数
4)调用print_str()函数时,直接传入一个 *参数会发生什么?
def print_str(first, *second):
print(first)
print(second)
控制台调用:
In [38]: numbers_strings = ("1","2","3","4","5")
...: print_str(*numbers_strings) # 注意这里的*numbers_strings
1
('2', '3', '4', '5')
说明:*numbers_strings出现在函数调用时,称为解包(一个“*”字符后面紧挨着1个tuple对象),numbers_strings自身是一个tuple对象,所以也称为元组的解包,tuple中的元素解包成一个一个的位置参数传入到函数中,所以才有下面两个语句的相等性!
print_str(*numbers_strings)
等同于
print_str("1","2","3","4","5")
5)未定义可变参数的函数被调用时,传入*参数会发生什么呢?
def print_str(first, second):
print(first)
print(second)
控制台调用:
In [40]: numbers_strings = ("1","2")
...: print_str(*numbers_strings)
1
2
print_str(*numbers_string)
等同于
print_str("1","2")
元组解包的过程中会将每一个元素依次放入到位置参数,这说明元组的解包功能的如下特点:
1、可以在可变参数中使用
2、也可以在未定义可变参数的函数上使用
元组解包功能是完全独立的一个功能
再次说明:*参数,出现在函数的不同的位置上时,具备不同的功能
1、当*参数出现在函数定义时,表示可变参数
2、当*参数出现在函数调用时,则表示解包功能
注意:解包tuple的时候,tuple的元素数量要与函数的位置参数总数一致
三、**kwargs例子
1)函数定义中,参数名称前有两个**
def printStr(**anything):
print(anything)
传入两个关键字参数调用printStr函数,看看发生什么?
In [42]: printStr(first = 5, second = 100)
{'first': 5, 'second': 100}
打印结果为dict对象的字符串形式,为什么anything成为dict了?
说明:函数调用时,传入的关键字参数有匹配的位置参数时,则位置参数优先使用(匹配)这些关键字参数,剩余所有未使用(未匹配)的关键字参数会在函数内组装进一个dict对象中,组装后dict对象会赋值给变量名anything,此时局部变量anything指向一个dict对象
注意:**参数只收集未匹配的关键字参数
2)函数调用时使用字典解包功能(dict对象前加**)
def printStr(first, **dict):
print(str(first) + "\n")
print(dict)
控制台调用:
In [44]: printDic = {"name": "tyson", "age":"99"}
...: printStr(100, **printDic)
100
{'name': 'tyson', 'age': '99'}
#等同于
In [45]: printDic = {"name": "tyson", "age":"99"}
...: printStr(100, name = "tyson", age = "99")
100
{'name': 'tyson', 'age': '99'}
说明:函数调用时,在一个dict对象的前面,添加**,表示字典的解包,它会把dict对象中的每个键值对元素,依次转换为一个一个的关键字参数传入到函数中。
四、总结
Python语法中,当*参数和**参数同时出现在函数定义的参数列表中时,说明参数列表可接受任意数量的参数,它们都统称为可变参数。
函数定义时
1、*args表示可接受任意个(包含0个)位置参数,当函数调用时,所有未使用(未匹配)的位置参数会在函数内自动组装进一个tuple对象中,此tuple对象会赋值给局部变量args
2、**kwargs表示可接受任意个(包含0个)关键字参数,当函数调用时,所有未使用(未匹配)的关键字参数会在函数内组装进一个dict对象中,此dict对象会赋值给局部变量kwargs
注意:函数定义时,二者同时存在,一定需要将*args放在**kwargs之前
函数调用时
1、*args表示解包元组对象中的每个元素作为位置参数传入到被调用函数中
2、**kwargs表示解包字典对象中的每个元素作为关键字参数传入到被调用函数中
注意事项
1、可变参数,可以传数量不定的多个参数,包括0个参数
2、可变参数,必须定义在普通参数(也称位置参数、必选参数、选中参数等名称)以及默认值参数的后面,这是因为可变参数会收集所有【未匹配】的参数,如果将可变参数定义在前面,那么普通参数与默认值参数就无法匹配到传入的参数,因为全都收集到可变参数中了。
def printStr(普通参数,默认值参数name="王员外",*参数,**参数):
pass
3、*参数必须定义在**参数的前面
def printStr(普通参数,*参数,**参数):
pass
4、调用包含*args参数的函数时,不要直接传入一个tuple对象,如果传入的是一个tuple对象,那么这个tuple对象只会成为未匹配的,函数内组装的tuple对象中一个元素而已。我们可以将tuple对象的元素使用元组解包语法传入,解包语法:*tuple。
temp = (1,2,3,4,5)
def my_first(*args):
print(args)
my_first(temp) #temp只算一个参数,除非你有这个需求
my_first(*temp) #OK
5、调用包含**kwargs参数的函数时,不要直接传入一个字典对象,一个字典对象只算一个参数,此时会报错,因为一个dict对象不符合关键字参数的语法规范,字典对象可以使用字典解包语法,解包语法: **dict
my_book = {"first":"小当家", "seoncd": "我是baby"}
def my_blood(**kwargs):
print(kwargs)
my_blood(my_book) #作为一个字典对象传入
my_blood(**my_book) #一个一个的关键字参数传入
6、*参数的变量名,一般使用变量名args,只是建议,你想叫啥名都行,它只是局部变量名
7、**参数的变量名,一般使用变量名kwargs,只是建议,你想叫啥名都行,它也是个局部变量名
一个包含位置参数、默认值参数,可变参数的函数(供参考)
def my_first_blood(first_name,last_name,age=0,*args,**kwargs):
print(first_name)
print(last_name)
print(age)
for ele in args:
print(ele)
for key,value in kwargs.items():
print(key)
print(value)
调用方式:
n [47]: my_first_blood("王", "员外")
王
员外
0
In [48]:
...: my_first_blood("王", "员外", 32)
王
员外
32
In [49]: my_first_blood("王", "员外", 32, "北京", "海淀", style="开心")
王
员外
32
北京
海淀
style
开心
一个仅传入关键字参数,导致报错的函数
def temp(say, **kwargs):
print(say)
print(kwargs)
temp(fk=100, hello=1000)
由于位置参数say,与传入的关键字参数fk和hello均不能匹配,所以此调用会导致报错。关键字参数必须得与位置参数的名称相匹配时,才能被位置参数使用.如下改变,即可:
In [52]: temp(10,fk=100, hello=1000)
10
{'fk': 100, 'hello': 1000}
解包功能不只是tuple、还有list、str、range
first = (1,2,3)
second = [1,2,3]
third = "123"
fourth = range(4)
print(*first)
print(*second)
print(*third)
print(*fourth)
五、再用例子系统说明
参考如下代码,通过例子,验证一遍上述的理论。
1. 带默认值的参数
在了解带星号(*)的参数之前,先看下带有默认值的参数,函数定义如下:
def defaultValueArgs(common, defaultStr = "default", defaultNum = 0):
print("Common args:", common)
print("Default String:", defaultStr)
print("Default Number:", defaultNum)
(1)带默认值的参数(defaultStr、defaultNum)不传参时的调用:
In [26]: defaultValueArgs("Test")
Common args: Test
Default String: default
Default Number: 0
(2)带默认值的参数(defaultStr、defaultNum),调用的时候可以直接传参(如下例中的defaultStr),也可以写成“argsName = value”的形式(如下例中的defaultNum):
In [27]: defaultValueArgs("Test", "Str", defaultNum=1)
Common args: Test
Default String: Str
Default Number: 1
In [28]: defaultValueArgs("Test", defaultNum=1)
Common args: Test
Default String: default
Default Number: 1
注意:在函数定义时,第一个带有默认值的参数之后的所有参数都必须有默认值,否则,运行时报错。
def defaultValueArgs(common, defaultStr = "default", defaultNum):
print("Common args:", common)
print("Default String:", defaultStr)
print("Default Number:", defaultNum)
编译输出:
SyntaxError: non-default argument follows default argument
2.带一个星号(*)的函数参数
带一个参数的函数定义如下:
def singalStar(common, *rest):
print("Common args: ", common)
print("Rest args: ", rest)
(1)带星号(*)的参数不传参:
In [3]: singalStar("hello")
Common args: hello
Rest args: ()
带星号(*)的参数不传参时默认是一个空的元组。
(2)带星号(*)的参数传入多个值时(个数大于或等于函数定义时的参数个数):
In [4]: singalStar("hello", "world", 000)
Common args: hello
Rest args: ('world', 0)
不难看出,第二种方式中,星号参数把接收的多个参数合并为一个元组。
(3)当我们直接传元组类型的值给星号参数时:
In [5]: singalStar("hello", ("world", 000))
Common args: hello
Rest args: (('world', 0),)
此时,传递的元组值作为了星号参数的元组中的一个元素。
(4)如果我们想把元组作为星号参数的参数值,在元组值前加上" * " 即可。
In [6]: singalStar("hello", *("world", 000))
Common args: hello
Rest args: ('world', 0)
In [7]: singalStar("hello", *("world", 000), "123")
Common args: hello
3.带两个星号(**)的函数参数
带两个星号(**)的函数定义如下:
(1)双星号(**)参数不传值:
In [9]: doubleStar("hello")
Common args: hello
Double args: {}
带双星号(**)的参数不传值时默认是一个空的字典。
(2)双星号(**)参数传入多个参数时(个数大于或等于函数定义时的参数个数):
In [10]: doubleStar("hello", "Test", 24)
TypeError: doubleStar() takes 1 positional argument but 3 were given
In [11]: doubleStar("hello", x = "Test", y = 24)
Common args: hello
Double args: {'x': 'Test', 'y': 24}
可以看到,双星号参数把接收的多个参数合并为一个字典,但与单星号不同的是,此时必须采用默认值传参的 “ args = value ” 的方式,“ = ” 前的字段成了字典的键,“ = ” 后的字段成了字典的值。
(3)如果想把字典作为星号参数的参数值,那么该怎么办呢?与单星号参数类似,在字典值前加上 “ ** ”,同时其后不能添加任何值。
In [12]: doubleStar("hello", {"name": "Test", "age": 24})
TypeError: doubleStar() takes 1 positional argument but 2 were given
In [13]: doubleStar("hello", **{"name": "Test", "age": 24}, {"name": "Test2", "age": 24})
SyntaxError: positional argument follows keyword argument unpacking
In [14]: doubleStar("hello", **{"name": "Test", "age": 24}, **{"name": "Test2", "age": 24})
TypeError: __main__.doubleStar() got multiple values for keyword argument 'name'
In [16]: doubleStar("hello", **{"name": "Test", "age": 24}, **{"name1": "Test1", "age1": 24})
Common args: hello
Double args: {'name': 'Test', 'age': 24, 'name1': 'Test1', 'age1': 24}
In [15]: doubleStar("hello", **{"name": "Test", "age": 24})
Common args: hello
Double args: {'name': 'Test', 'age': 24}
4. 在有些情况下,单星号函数参数和双星号函数参数是一起使用的:
In [17]: def singalAndDoubleStar(common, *single, **double):
...: print("Common args: ", common)
...: print("Single args: ", single)
...: print("Double args: ", double)
In [18]: singalAndDoubleStar("hello")
Common args: hello
Single args: ()
Double args: {}
In [19]: singalAndDoubleStar("hello", "world", 000)
Common args: hello
Single args: ('world', 0)
Double args: {}
In [20]: singalAndDoubleStar("hello", "world", 000, {"name": "Test", "age": 24})
Common args: hello
Single args: ('world', 0, {'name': 'Test', 'age': 24})
Double args: {}
In [21]: singalAndDoubleStar("hello", "world", 000, **{"name": "Test", "age": 24})
Common args: hello
Single args: ('world', 0)
Double args: {'name': 'Test', 'age': 24}
In [22]: singalAndDoubleStar("hello", ("world", 000), {"name": "Test", "age": 24})
Common args: hello
Single args: (('world', 0), {'name': 'Test', 'age': 24})
Double args: {}
In [23]: singalAndDoubleStar("hello", *("world", 000), {"name": "Test", "age": 24})
Common args: hello
Single args: ('world', 0, {'name': 'Test', 'age': 24})
Double args: {}
In [24]: singalAndDoubleStar("hello", *("world", 000), **{"name": "Test", "age": 24})
Common args: hello
Single args: ('world', 0)
Double args: {'name': 'Test', 'age': 24}
参考文章
1、Python带*参数和带**参数_猫学学的博客-优快云博客_python带*的参数
2、python函数参数中的**_kwargspython函数参数中的**_
3、Python之可变参数,*参数,**参数,以及传入*参数,**参数解包,*args,**kwargs的理解_叫我王员外就行的博客-优快云博客_python 参数**
4、https://www.jb51.net/article/206158.htm
5、Python之可变参数,*参数,**参数,以及传入*参数,**参数解包,*args,**kwargs的理解_叫我王员外就行的博客-优快云博客_python 参数**