【Python】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 例子

1. 包含两个位置参数的函数print_str

def print_str(first, second):
    print(first)
    print(second)

只传1个参数调用print_str()函数,会发生什么呢?

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()函数,看看这次发生什么?

print_str("hello")

输出结果为:

hello
()

这次不再报错,传入的第一个字符串参数"hello"打印出来了,没有传入参数的*second则打印的是一个tuple对象的字符串表示形式,即一个括号"()" 。 注意:()表示含有0个元素的tuple对象!

思考:为什么second变量变成一个tuple对象了?

3. 为print_str()函数传入四个参数…会发生什么?

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)
numbers_strings = ("1","2","3","4","5")
print_str(*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)
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函数,看看发生什么?

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)
printDic = {"name": "tyson", "age":"99"}
printStr(100, **printDic)

输出结果为:

100
{'name': 'tyson', 'age': '99'}

等同于:

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旅途中的宽~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值