Python带*参数和带**参数:可变参数

一、理论解释:

*参数与**参数是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带*参数和带**参数_猫学学的博客-CSDN博客_python带*的参数

2、python函数参数中的**_kwargspython函数参数中的**_

3、Python之可变参数,*参数,**参数,以及传入*参数,**参数解包,*args,**kwargs的理解_叫我王员外就行的博客-CSDN博客_python 参数**​​​​​​

4、https://www.jb51.net/article/206158.htm

5、Python之可变参数,*参数,**参数,以及传入*参数,**参数解包,*args,**kwargs的理解_叫我王员外就行的博客-CSDN博客_python 参数** 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340