社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【Python】函数中的*args和**kwargs是个什么东西?

小白学视觉 • 2 年前 • 233 次点击  

点击上方小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达


前言 本文介绍了*args和**kwargs的详细用法。

作者:成老师@知乎(已授权转载)

编辑:CV技术指南

原文:https://zhuanlan.zhihu.com/p/479358658

1. 概述


在Python代码中,经常见到函数中有*args和**kwargs写法,它们都起到了可选参数(optional arguments)的作用。那么具体怎么使用呢?且看下文细细分解。

2. *和**


在了解*args和**kwargs的作用之前,首先要理解*和**在Python中的使用。*和**主要有三方面的用途,(一)是对可迭代对象进行拆分,(二)可变变量的赋值,(三)函数的可选参数标志。

2.1 用于对可迭代对象进行拆分

在Python中,一切内置了__iter__方法的对象都是可迭代对象,典型的可迭代对象包括元组(tuple)、列表(list)、集合(set)、字典(dict)等等。这种拆解主要运用在函数的参数语境中,以print函数为例,以下5个表达式是完全等价的:

print("a""b""c")                
print(*("a""b""c"))             # 元组 
print(*["a""b""c"])             # 列表
print(*{"a"1"b"2"c"3})    # 注意:对字典拆解时只拆解key
print(*"abc")                       # 注意:字符串也是可迭代对象

输出结果完全一样(如下)。但是要注意的是,集合不太一样,因为集合是无序的,所以print(*{"a", "b", "c"})的时候"a", "b", "c"可能是乱序的。

a b c
a b c
a b c
a b c
a b c

除了函数参数语境,赋值语境下也可以对可迭代对象进行拆分,如

a = *range(3),
b = *range(3), 3
c = [*range(3)]
d = {*range(3)}
e = {*{'y'1}}     # 只对key进行拆解
f = {**{'y'1}}    # 对key和value都进行了拆解


print(a)  # output: (0,1,2)
print(b)  # output: (0,1,2,3)
print(c)  # output: [0,1,2]
print(d)  # output: {0,1,2}
print(e)  # output: {'y'}
print(f)  # output: {'y':1}

但是这种用法只能在元组、列表、集合和字典内部使用,其他地方就会报错,比如

a = *range(3),   # 这么写默认是在元组内部,即等效于a = (*range(3), )
a = *range(3)    # 这么写就会报错


2.2 可变变量的赋值

如果对于一个可迭代对象l = [1, 2, 3, 4, 5, 6],如果我想把第一个元素赋值给变量a,而把剩下的元素通通赋值给变量b,该怎么写呢?你可能会想这么写

l = [123456]
a = l[0]
b = l[1:]

这么写有两个问题:(一)设想一个更复杂的情况,如果我想把第一个元素赋值给变量a,最后一个元素赋值给c,而把剩下的元素统统赋值给变量b,那么代码将变得又臭又长;(二)这么写对于unsubscriptable的对象是不适用的,比如集合。用*可以非常优雅地解决这个问题,如

a, *b, c = {123456}

2.3 函数的可选参数标志

理解了前面两点,这一点其实就是上面两点的综合运用。如果是单星号*标记的就是可选的位置参数(positional arguments),如果是双星号标记的就是可选的关键词参数(keyword arguments),如

def function(a, *args, **kwargs):
    print(a)
    print(args)
    print(kwargs)

function(123, c=4)

输出结果如下

1
(23)
{'c'4}

3. args: positional arguments


args指的是可选的位置参数,这个东西有什么用呢?举个简单的例子我想自定义一个函数求两个元素的和,可以这么写

def my_sum_function(a, b):
    print(a + b)

my_sum_function(12)

可是如果我想求三个元素的和呢,那是不是要这样写?

def my_sum_function(a, b, c):
print(a + b + c)

my_sum_function(123)

你可能已经意识到了,就是这种写函数的思路扩展性非常差,对传入参数的个数有着严格的要求。*args可以解决这一问题,如

def my_sum_function(*args):
    print(sum(args))

# 对于这个函数,不管传入多少个参数都是可以的
my_sum_function(1)
my_sum_function(12)
my_sum_function(123)

4. kwargs: keyword arguments


既然已经有了可选的位置参数(args),还要可选的关键词参数(kwargs)干嘛呢?关键词参数相当于给参数一个关键词,有着特定的用途,关键词对这个特殊用途进行标识。由于需要关键词,因此kwargs的传入函数的类型是字典。

比如我想定义一个函数:这个函数计算传入参数的和,但是如果我传入的参数中包含分母(denominator),就会在这个和的基础上再除以分母。在这种情形下,这个分母(denominator)不一定需要,因此是可选参数。同时,有区别于其他的位置参数,因此需要加一个关键词。这个函数可以这么写

def self_defined_function(*args, **kwargs):
if "denominator" not in kwargs:
print(sum(args))
else:
# 注意:kwargs的类型是字典
denominator = kwargs["denominator"]
print(sum(args) / denominator)

# 没有denominator时,打印几个数的和
self_defined_function(123)
# 有denominator时,先求几个数的和,再除以denominator
self_defined_function(123, denominator=3)

当然,这个函数有更优雅的写法,就是用kwargs.get()或者kwargs.pop(),如

def self_defined_function(*args, **kwargs):
denominator = kwargs.get("denominator"1)
print(sum(args) / denominator)

self_defined_function(123)
self_defined_function(123, denominator=3)

kwargs.get("denominator", 1)会从kwargs这个字典中读取"denominator"这个key对应的值,如果没有这个key,就会返回1。这样写可以避免不断地写if else语句。

kwargs.pop()与kwargs.get()不同的地方在于,kwargs.pop()读取某个key对应的值后,kwargs这个字典中的这个键值对就会被删除。

5. 注意


在Python中默认的函数参数顺序是:必选参数、默认参数、*args和**kwargs。如




    
def self_defined_function(name, purpose="Demo", *args, **kwargs):
    # name是必选参数
    # purpose是默认参数

    print("Name: ", name)
    print("Purpose: ", purpose)
    denominator = kwargs.pop("denominator"1)
    print(sum(args) / denominator)

# 这里"Demo"不能少
self_defined_function("A self-defined function""Demo"123, denominator=3)

在这种写法中,你会发现"Demo"不能少,如果少了"Demo",purpose就变成了1。同时你又不能下面这样写,因为这样不符合传参的规范

# 不能这样写,这不符合传参的规范
self_defined_function("A self-defined function", purpose="Demo"123, denominator=3)

这样程序会报错。既然无论如何都必须要把"Demo"参数写出来,那么默认参数就失去了意义。因此在Python3中放松了对顺序的限制[2],只要**kwargs放在最后就行,剩下三个(必选参数、默认参数、*args)的顺序不做严格规定,因此上述问题就可以得到解决

def self_defined_function(name, *args, purpose="Demo", **kwargs):
    # name是必选参数
    # purpose是默认参数

    print("Name: ", name)
    print("Purpose: ", purpose)
    denominator = kwargs.pop("denominator"1)
    print(sum(args) / denominator)

# 默认参数purpose终于不需要专门定义了
self_defined_function("A self-defined function"123, denominator=3)

好消息!

小白学视觉知识星球

开始面向外开放啦👇👇👇




下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲
小白学视觉公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲
小白学视觉公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群


欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器 自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/152568
 
233 次点击