python - 一篇了解参数 args,kwargs 的文章
函数传参是最常用的方法,但是你真的掌握python里参数的传递和使用了吗?之前文章我们介绍了传参的拷贝情况,会不会引起传入参数的变化。本文详细介绍python的函数中*args, **kwargs的使用。
一 *在python中的作用
首先我们了解下python里*操作符主要有哪些作用。
1. 数学运算符
常用的数学操作符,比如乘法day_seconds = 24*60*60,次方 5**2=25
2. 解包,收集列表中多余的值
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
3. 函数定义和函数调用
本文重点就是介绍*的第三个作用:在函数定义和调用的使用。
在定义函数时,*代表收集参数,**代表收集关键字参数;
在调用函数时,*和**都是分配参数用的。
* args 和 ** kwargs 主要用于函数定义,你可以将不定数量的参数传递给一个函数。这里不定的意思是: 预先并不知道,函数使用者会传递多少个参数给你,所在在这个场景下使用这两个关键字。
*args (arguments)表示任何多个无名参数, 它本质上是一个 tuple
** kwargs (keyword arguments)表示关键字参数, 它本质上是一个 dict
注意:使用时必须要求 *args 参数列要在** kwargs 前面 【因为位置参数在关键字参数的前面。】
二 args 和 ** kwargs的用法实例
下面我们用一些实例来熟悉* args 和 ** kwargs的用法。
1. arg参数
最简单的传参用法, 参数个数和位置意义对应,但是如果是不定长参数个数,比如配置项,这种传参方法就不适用了
def test_arg(x, y, z):
print("test_arg", x, y ,z)
test_arg(1, 2, 3)
2. *args不定长参数个数
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]) <class 'tuple'>
test_args arg x
test_args arg y
test_args arg 1
test_args arg 2
test_args arg 3
test_args arg [1.0]
也可以直接传入一个变量,那传入args和解包后的*args区别是什么呢
args = 1, 2, 3
test_args(args)
输出:
test_args args ((1, 2, 3),) <class 'tuple'>
test_args arg (1, 2, 3)
输出:
test_args(*args)
test_args args (1, 2, 3) <class 'tuple'>
test_args arg 1
test_args arg 2
test_args arg 3
知识点:args = 1, 2, 3 是元组类型,做为元组类型作为参数传递,不解包就是一个整体;所以传入元组参数应该传入解包后的*args
3. **kargs变长的带关键字参数
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]} <class 'dict'>
test_kargs a a
test_kargs b b
test_kargs c 1
test_kargs d [1, 2]
知识点:kargs是字典类型,传入字典参数应该传入解包后的**kargs
4. arg和*arg混用
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后面的变量参数名
5. *args 和 **kargs 混合使用
def test_args_kwargs_case1(*args, **kwargs):
print("args", args, type(args))
print("kwargs", kwargs, type(kwargs))
args = 1, 2, 3
kargs = {"arg1":1, "arg2":2,"arg3":3}
test_args_kwargs_case1(1, 2, 3, arg1 = 1, arg2= 2, arg3 = 3)
输出:
args (1, 2, 3) <class 'tuple'>
kwargs {'arg1': 1, 'arg2': 2, 'arg3': 3} <class 'dict'>
test_args_kwargs_case1(args, kargs)
输出:
args ((1, 2, 3), {'arg1': 1, 'arg2': 2, 'arg3': 3}) <class 'tuple'>
kwargs {} <class 'dict'>
注意所有参数作为一个tuple传给了*args
test_args_kwargs_case1(args, **kargs)
输出:
args ((1, 2, 3),) <class 'tuple'>
kwargs {'arg1': 1, 'arg2': 2, 'arg3': 3} <class 'dict'>
注意,后面的**kargs作为无名参数字典传入,前面传入的是未解包的args元组
test_args_kwargs_case1(*args, **kargs)
输出:
args (1, 2, 3) <class 'tuple'>
kwargs {'arg1': 1, 'arg2': 2, 'arg3': 3} <class 'dict'>
正确的传入方式
知识点0:传入的是解包后的*args,作为带关键字参数,才会输出args (1, 2, 3) <class 'tuple'>
知识点1:传入的是解包后的**kargs,作为带关键字参数,才会输出kwargs {'arg1': 1, 'arg2': 2, 'arg3': 3} <class 'dict'>
知识点2:顺序:必须*args在**kargs之前 test_args_kwargs_case2(**kwargs, *args) 编译报错 * parameter after ** parameter
6. arg, *args 和 **kargs混用
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,3
kwargs = {"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 + num3
sum = test_default_value(1)
知识点:带默认值参数的后面都需要带默认值,参数顺序:不带默认值,带默认值
from typing import Optional
def test_option_type(num1: int, num2: int,num3: Optional[int] = None):
return num1 + num2 + num3
知识点:
规定默认值时,不一定要声明变量所属的类型(说到底Python中的对象、变量名只是一个指针或者说地址罢了),Python是一门动态语言,它总会在Python解释器进程运行的时候去动态地判定一个变量赋值的类型,而之所以在代码中声明静态类型则是为了减少人为错误而提供相应的类型或错误提示,但并不会影响Python的运行!
from typing import Optional
def 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.****.net/qq_44683653/article/details/108990873
https://blog.****.net/zkk9527/article/details/88675129
https://blog.****.net/GODSuner/article/details/117961990
上一篇: Android ADK 编程入门
下一篇: 安卓编译优化 - dex2oat 编译
推荐阅读
-
[姿势估计] 实践记录:使用 Dlib 和 mediapipe 进行人脸姿势估计 - 本文重点介绍方法 2):方法 1:基于深度学习的方法:。 基于深度学习的方法:基于深度学习的方法利用深度学习模型,如卷积神经网络(CNN)或递归神经网络(RNN),直接从人脸图像中学习姿势估计。这些方法能够学习更复杂的特征表征,并在大规模数据集上取得优异的性能。方法二:基于二维校准信息估计三维姿态信息(计算机视觉 PnP 问题)。 特征点定位:人脸姿态估计的第一步是通过特征点定位来检测和定位人脸的关键点,如眼睛、鼻子和嘴巴。这些关键点提供了人脸的局部结构信息,可用于后续的姿势估计。 旋转表示:常见的旋转表示方法包括欧拉角和旋转矩阵。欧拉角通过三个旋转角度(通常是俯仰、偏航和滚动)描述头部的旋转姿态。旋转矩阵是一个 3x3 矩阵,表示头部从一个坐标系到另一个坐标系的变换。 三维模型重建:根据特征点的定位结果,三维人脸模型可用于姿势估计。通过将人脸的二维图像映射到三维模型上,可以估算出人脸的旋转和平移信息。这就需要建立人脸的三维模型,然后通过优化方法将模型与特征点对齐,从而获得姿势估计结果。 特征点定位 特征点定位是用于检测人脸关键部位的五官基础部分,还有其他更多的特征点表示方法,大家可以参考我上一篇文章中介绍的特征点检测方案实践:人脸校正二次定位操作来解决人脸校正的问题,客户在检测关键点的代码上略有修改,坐标转换部分客户见上图 def get_face_info(image). img_copy = image.copy image.flags.writeable = False image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_detection.process(image) # 在图像上绘制人脸检测注释。 image.flags.writeable = True image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) box_info, facial = None, None if results.detections: for detection in results. for detection in results.detections: mp_drawing.Drawing.detection = 无 mp_drawing.draw_detection(image, detection) 面部 = detection.location_data.relative_keypoints 返回面部 在上述代码中,返回的数据是五官(6 个关键点的坐标),这是用 mediapipe 库实现的,下面我们可以尝试用另一个库:dlib 来实现。 使用 dlib 使用 Dlib 库在 Python 中实现人脸关键点检测的步骤如下: 确保已安装 Dlib 库,可使用以下命令: pip install dlib 导入必要的库: 加载 Dlib 的人脸检测器和关键点检测器模型: 读取图像并将其灰度化: 使用人脸检测器检测图像中的人脸: 对检测到的人脸进行遍历,并使用关键点检测器检测人脸关键点: 显示绘制了关键点的图像: 以下代码将参数 landmarks_part 添加到要返回的关键点坐标中。
-
python - 一篇了解参数 args,kwargs 的文章
-
在一篇文章中了解 Python 进程间通信的几种方式
-
Python中的动态参数*args和**kwargs:聚合与拆分的魔法
-
Python的动态参数:*args, **kwargs与命名空间解析