函数传参是最常用的方法,但是你真的掌握python里参数的传递和使用了吗?之前文章我们介绍了传参的拷贝情况,会不会引起传入参数的变化。本文详细介绍python的函数中*args, **kwargs的使用。
首先我们了解下python里*操作符主要有哪些作用。
(资料图片)
常用的数学操作符,比如乘法day_seconds = 24*60*60,次方 5**2=25
def test_splat(): a = [1, 2, 3] # 这里*a之将a解包到新列表 b = [*a, 4, 5, 6] # [1, 2, 3] print("splat list a", a) # 1, 2, 3 print("splat list *a", *a) # [1,2,3,4,5,6] print("splat list b", b) val_1, val_2, *list_3 = b # 1 print("splat val_1", val_1) # 2 print("splat val_2", val_2) # [3, 4, 5, 6] print("splat list_3", list_3)
如上代码所示,*a就是将列表[1,2,3]解包为1,2,3
本文重点就是介绍*的第三个作用:在函数定义和调用的使用。
在定义函数时,*代表收集参数,**代表收集关键字参数;
在调用函数时,*和**都是分配参数用的。
* args 和 ** kwargs 主要用于函数定义,你可以将不定数量的参数传递给一个函数。这里不定的意思是: 预先并不知道,函数使用者会传递多少个参数给你,所在在这个场景下使用这两个关键字。
*args (arguments)表示任何多个无名参数, 它本质上是一个 tuple
** kwargs (keyword arguments)表示关键字参数, 它本质上是一个 dict
注意:使用时必须要求 *args 参数列要在** kwargs 前面 【因为位置参数在关键字参数的前面。】
下面我们用一些实例来熟悉* args 和 ** kwargs的用法。
最简单的传参用法, 参数个数和位置意义对应,但是如果是不定长参数个数,比如配置项,这种传参方法就不适用了
def test_arg(x, y, z): print("test_arg", x, y ,z)test_arg(1, 2, 3)
def test_args(*args): print("test_args args", args, type(args)) for arg in args: print("test_args arg", arg)
我们可以将参数直接传入
test_args("x", "y", 1, 2, 3, [1.0])输出:test_args args ("x", "y", 1, 2, 3, [1.0]) test_args arg xtest_args arg ytest_args arg 1test_args arg 2test_args arg 3test_args arg [1.0]
也可以直接传入一个变量,那传入args和解包后的*args区别是什么呢
args = 1, 2, 3test_args(args)输出:test_args args ((1, 2, 3),) test_args arg (1, 2, 3)输出:test_args(*args)test_args args (1, 2, 3) test_args arg 1test_args arg 2test_args arg 3
知识点:args = 1, 2, 3 是元组类型,做为元组类型作为参数传递,不解包就是一个整体;所以传入元组参数应该传入解包后的*args
def test_kargs(**kargs): print("test_kargs kargs", kargs, type(kargs)) for key,item in kargs.items(): print("test_kargs", key,item)
test_kargs(a="a", b="b", c=1, d=[1,2]) kargs = (a="a", b="b", c=1, d=[1,2]}) test_kargs(**kargs) 输出: test_kargs kargs {"a": "a", "b": "b", "c": 1, "d": [1, 2]} test_kargs a atest_kargs b btest_kargs c 1test_kargs d [1, 2]
知识点:kargs是字典类型,传入字典参数应该传入解包后的**kargs
def test_arg_args_case1(x, y, *args): print("test_arg_args x", x) print("test_arg_args y", y) print("test_arg_args args", args)def test_arg_args_case2(x, *args, y): print("test_arg_args x", x) print("test_arg_args y", y) print("test_arg_args args", args) test_arg_args_case1("x", "y", 1, "A", [1, 2])test_arg_args_case2("x", 1, "A", [1, 2], y="y")
知识点1:按照位置,*args在最后,前面是对应位置的参数,剩下的为变长*args
知识点2:如果*args不在最后,则需要在参数传入时,明确定义 *args后面的变量参数名
def test_args_kwargs_case1(*args, **kwargs): print("args", args, type(args)) print("kwargs", kwargs, type(kwargs))
args = 1, 2, 3kargs = {"arg1":1, "arg2":2,"arg3":3}test_args_kwargs_case1(1, 2, 3, arg1 = 1, arg2= 2, arg3 = 3)输出:args (1, 2, 3) kwargs {"arg1": 1, "arg2": 2, "arg3": 3} test_args_kwargs_case1(args, kargs)输出:args ((1, 2, 3), {"arg1": 1, "arg2": 2, "arg3": 3}) kwargs {} 注意所有参数作为一个tuple传给了*argstest_args_kwargs_case1(args, **kargs)输出:args ((1, 2, 3),) kwargs {"arg1": 1, "arg2": 2, "arg3": 3} 注意,后面的**kargs作为无名参数字典传入,前面传入的是未解包的args元组test_args_kwargs_case1(*args, **kargs)输出:args (1, 2, 3) kwargs {"arg1": 1, "arg2": 2, "arg3": 3} 正确的传入方式
知识点0:传入的是解包后的*args,作为带关键字参数,才会输出args (1, 2, 3)
知识点1:传入的是解包后的**kargs,作为带关键字参数,才会输出kwargs {"arg1": 1, "arg2": 2, "arg3": 3}
知识点2:顺序:必须*args在**kargs之前 test_args_kwargs_case2(**kwargs, *args) 编译报错 * parameter after ** parameter
def test_arg_args_kwargs1(first_arg, *args, second_arg , **kwargs): print("first_arg", first_arg) print("second_arg", second_arg) print("args", args[0], args[1], args, type(args)) print("kwargs", kwargs, type(kwargs))def test_arg_args_kwargs2(*args, first_arg, second_arg , **kwargs): print("first_arg", first_arg) print("second_arg", second_arg) print("args", args[0], args[1], args, type(args)) print("kwargs", kwargs, type(kwargs))def test_arg_args_kwargs3(first_arg, second_arg ,*args, **kwargs): print("first_arg", first_arg) print("second_arg", second_arg) print("args", args[0], args[1], args, type(args)) print("kwargs", kwargs, type(kwargs))
args = 1,2,3kwargs = {"arg1":1, "arg2":2,"arg3":3}# 报错,必须是无关键字在有关键字之前# test_arg_args_kwargs1(first_arg=1, *args, second_arg=2, **kwargs)test_arg_args_kwargs2(*args, first_arg=1, second_arg=2, **kwargs)test_arg_args_kwargs3(1, 2, *args, **kwargs)
知识点1:顺序必须是无关键字在有关键字之前
比如:*args, 带关键字arg, **kwargs; arg, *args, **kwargs
建议最好按arg, *args, **kwargs的顺序传入
有时候传入参数会使用一些默认值参数,这里也简单介绍下默认值的使用规则。
def test_default_value(num1, num2=2, num3=3): return num1 + num2 + num3sum = test_default_value(1)
知识点:带默认值参数的后面都需要带默认值,参数顺序:不带默认值,带默认值
from typing import Optionaldef test_option_type(num1: int, num2: int,num3: Optional[int] = None): return num1 + num2 + num3
知识点:
规定默认值时,不一定要声明变量所属的类型(说到底Python中的对象、变量名只是一个指针或者说地址罢了),Python是一门动态语言,它总会在Python解释器进程运行的时候去动态地判定一个变量赋值的类型,而之所以在代码中声明静态类型则是为了减少人为错误而提供相应的类型或错误提示,但并不会影响Python的运行!
from typing import Optionaldef test_option_type(num1: int, num2: int,num3: Optional[int] = None): return num1 + num2 + num3
知识点:
可选类型,作用几乎和带默认值的参数等价,不同的是使用Optional会告诉你的IDE或者框架:这个参数除了给定的默认值外还可以是None,而且使用有些静态检查工具如mypy时,对 a: int =None这样类似的声明可能会提示报错,但使用a :Optional[int] = None不会。
如果我们要返回多个参数,一般是如下写法
def test_return_args(): return 1,2,3,4,5,6
a,b,c,d,e,f = test_return_args()print(a,b,c,d,e,f)a, b, _, _, _, _ = test_return_args()print(a, b,_)a, b, *_ = test_return_args()print(a, b, *_)
如果是多个参数中,我们不同调用时刻是想用不同的参数,其他属于冗余参数,可以使用_定义。
如果嫌弃太多冗余太麻烦,就可以使用*_将多余参数进行压包。
那万一我有时候想要a,b,e呢。。还是要很多冗余_,代码不易理解。这里就可以使用具名元组。
def test_namedtuple(): # 构建具名元组 Point = namedtuple("Point", ["x", "y", "z", "value"]) # 添加值,实例化 p1 = Point(1, 1, 1, 5) p2 = Point(x=1, y=2, z=4, value=4) # 解包字典 dict = {"x": 1, "y": 2, "z": 3, "value":4} point = Point(**dict) return point
# 多个返回参数,可以使用具名元组point = test_namedtuple()print("out", point, point[0], point.x)输出:out Point(x=1, y=2, z=3, value=4) 1 1
知识点:namedtuple比dict的优点是,其具有有序性,不可变只读。
参考:
https://blog.csdn.net/qq_44683653/article/details/108990873
https://blog.csdn.net/zkk9527/article/details/88675129
https://blog.csdn.net/GODSuner/article/details/117961990
标签:
Copyright © 2015-2022 海峡兽药网版权所有 备案号:皖ICP备2022009963号-10 联系邮箱:396 029 142 @qq.com