在Python中的代码中经常会见到 *args**kwargsargs 是 arguments 的缩写,*args表示位置参数;kwargs 是 keyword arguments 的缩写,**kwargs表示关键字参数。
  args 与 kwargs 只是变量名,可以自定义其他变量名,重点在于单星号(*)和双星号(**)的解包操作符。

目录

  • 1. 什么是解包
  • 2. 解包的使用
    • 2.1 无星号解包操作
      • 2.1.1 可迭代对象的解包
      • 2.1.2 多变量赋值
    • 2.2 * 解包操作
      • 2.2.1 可迭代对象的解包
      • 2.2.2 函数调用时的解包操作
    • 2.3 ** 解包操作
      • 2.3.1 可迭代对象的解包
      • 2.3.2 函数调用时的解包操作
    • 2.4 函数定义中 * 和 ** 的混合使用
  • 3. 解包的应用
    • 3.1 * 的应用
      • 3.1.1 列表转元组巧妙方法
      • 3.1.2 实现类似矩阵转置的操作
      • 3.1.2 切分嵌套可迭代对象数据
    • 3.2 * 和 ** 的应用之装饰器
  • 4. 一些补充
    • 4.1 函数传参时,可变参数(*)前不能指定参数名传参
    • 4.2 设计调用函数时必须使用参数名传参
    • 4.3 函数定义时,关键字参数只能作为最后一个参数
    • 4.4 _ 与 * 赋值的区别

1. 什么是解包

  • 解包(Unpacking)就是把一个容器拆开、分解,在Python中的解包是自动完成的。
  • 解包操作可以应用于任何可迭代对象,如元组、字典、列表、集合、字符串、生成器等。
  • 在Python3中,解包操作符有 * 和 **,一般情况下:
    • *:用于列表、元组、集合
    • **:用于字典

2. 解包的使用

2.1 无星号解包操作

在Python2中的解包操作是无星号的自动解包,即没有解包操作符,要求 “变量数量 = 元素数量” 。

2.1.1 可迭代对象的解包

① 列表解包

>>> a, b, c = [1,2,3]
>>> a
1
>>> b
2
>>> c
3

② 元组解包

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3>>> a,b,c = enumerate(['a', 'b', 'c'])
>>> a
(0, 'a')

③ 字符串解包

>>> a,b,c = "abc"
>>> a
'a'
>>> b
'b'
>>> c
'c'

④ 字典解包

注意:解包后,只会把字典的 key 取出来,value 丢掉了。

>>> a,b,c = {"a":1, "b":2, "c":3}
>>> a
'a'
>>> b
'b'
>>> c
'c'

⑤ 生成器解包

>>> a,b,c = (x + 1 for x in range(3))
>>> a
1
>>> b
2
>>> c
3

2.1.2 多变量赋值

① 对多个变量同时赋值

>>> a, b = 1, 2
>>> a
1
>>> b
2

上述本质是自动解包的过程,等号右边其实是一个元组对象 (1, 2)

注意:对单个变量赋值数值时,若等号右边的代码不小心多了一个逗号 “,” 就变成了赋值元组对象,因此写代码的时候需要特别注意!

>>> a = 1,
>>> a
(1,)

② 交换两个变量

>>> a, b = 1, 2
>>> a, b = b, a
>>> a
2
>>> b
1

③ 接收函数返回的多个值

def test():return 1, 2, 3a, b, c = test()
print(a, b, c)   # 1 2 3

  函数的返回值是一个元组,左侧是三个变量,这样就会发生解包,a, b, c 依次等于元组里的元素,函数的返回值有3个,被封包成了元组。

Python3中,开始支持更高级的解包操作——星号解包操作( * 和 **),实现了 “变量数量 < 元素数量” 的解包。这个特性可以在 PEP 3132 中看到。

2.2 * 解包操作

2.2.1 可迭代对象的解包

  • 收集元素

  “变量数量 < 元素数量” 情况下,在 任意一个变量面前加一个星号,分配元素时,每个不带星号的变量优先分配一个元素后,剩下的元素都分配给带星号变量。星号变量收集的元素将放入一个列表list内。

>>> a, b, *c = (1,2,3,4)
>>> a
1
>>> b
2
>>> c
[3, 4]

相较于下例切片操作赋值,* 解包使得代码更简洁。

>>> n = [1,2,3,4]
# 使用切片操作
>>> a, b, c = n[0], n[1:-1], n[-1]
>>> a
1
>>> b
[2, 3]
>>> c
4
  • 取出元素
>>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}

注:单星号 * 对字典解包时,只能读取到字典中的键(key)。如下所示:

d = {'x': 1, 'y': 2}
print(*d)  # 输出 x y
  • 拼接元素(取出元素的应用)
>>> list1 = [1,2,3]
>>> list2 = range(3,6)
>>> [*list1, *list3]
[1, 2, 3, 3, 4, 5]

*相较于 +
  虽然列表之间可以用 + 进行拼接,但是 list 类型无法与 range 对象直接通过+ 拼接,必须先将 list2 强制转换为 list 对象才能做 + 拼接。* 使得我们的代码更加优雅!

2.2.2 函数调用时的解包操作

函数调用时,一个星号可作用于所有的可迭代对象,称为迭代器解包操作,作为位置参数传递给函数。

  • 一个单星号的使用

定义如下 func 函数,函数中有三个位置参数 a,b,c,调用该函数必须传入三个参数。

def func(a,b,c):print(a,b,c)>>> func(1,2,3)
1 2 3

也可运用 * 解包含有3个元素的可迭代对象,作为参数传递

>>> func(*[1,2,3])   # 可迭代对象:列表
1 2 3
>>> func(*(1,2,3))   # 可迭代对象:元组
1 2 3
>>> func(*"abc")     # 可迭代对象:字符串
a b c
>>> func(*{"a":1,"b":2,"c":3})    # 可迭代对象:字典
a b c
  • 多个单星号的使用

在Python 3.5 之前的版本,函数调用时一个函数中解包操作只允许一个 * 和 一个 **,而从 3.5 开始,PEP 448 对解包操作做了进一步的扩展,在函数调用中,可以有任意多个解包操作。

如Python 3.4 中,print 函数 不允许多个 * 操作

>>> print(*[1,2,3], *[3,4])File "<stdin>", line 1print(*[1,2,3], *[3,4])^
SyntaxError: invalid syntax

Python 3.5 以上版本以使用任意多个解包操作

>>> print(*[1], *[2], 3)
1 2 3

2.3 ** 解包操作

2.3.1 可迭代对象的解包

  • 取出元素
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}

注意:在 解包多个字典放入新的字典 时,对相同的键,后者对应的值会覆盖(override)前者对应的值。

>>> {'x': 1, **{'x': 2}}
{'x': 2}>>> {**{'x': 2}, 'x': 1}
{'x': 1}

注意:调用函数过程中,通过解包多个字典传参时,解包后不允许存在相同关键字,如 f(**{‘x’: 2}, **{‘x’: 3})

  • 拼接元素(取出元素的应用)
>>> a = {"a":1, "b":2}
>>> b = {"c":3, "d":4}
>>> {**a, **b}
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

2.3.2 函数调用时的解包操作

函数调用时,两个星号的解包操作只能作用于字典对象,称之为字典解包操作。解包时会自动解包字典对象成关键字参数 key=value 的格式,传递给函数。

def func(a,b,c):print(a,b,c)>>> func(**{"a":1,"b":2,"c":3})
1 2 3

相较于以下方式,** 解包的好处是节省代码量,使代码看起来更优雅。

>>> d = {"a":1, "b":2, "c":3}
>>> func(a = d['a'], b=d['b'], c=d['c'])
1 2 3

注意:** 解包字典后是 key = value的键值格式。上例字典的 key 值正好 是 func 函数的形参值。但是 print 函数是不能直接打印 ** 解包字典的结果的,如下所示:

c = {"name": 'zhang', "age": 2}
print(**c)

TypeError: 'name' is an invalid keyword argument for print()

  由于 ** 解包出来的数据是这样的 :**c = name=‘zhang’,age=2。且 print 函数的源码如下:

def print(self, *args, sep=' ', end='\n', file=None): # known special case of print 只支持 *args,不支持 **kwargs。因此尝试写成如下形式进行打印输出。

>>> print("Name:{name}, Age:{age}".format(**c))
Name:zhang, Age:2

2.4 函数定义中 * 和 ** 的混合使用

  • *args 与 **kwargs 是 Python 中 可变参数 的两种形式。
  • 在函数定义中位置参数必须在关键字参数的前面(即*args,**kwargs的顺序)。
  • 在函数定义中,形参的 *args 是接受位置参数的 args,args 是一个元组,把传入的数据放进其中;形参的 **kwargs 是接受关键字参数的 kwargs,kwargs 是一个字典,把传入关键字键值放进其中。
  • 函数调用传入实参时,可变参数(*)之后的参数必须指定参数名传参,否则将被归到可变参数(*)之中

  如下所示,*args 收集位置参数后传递一个可变参数元组 给函数实参,**para 收集关键字参数后传递一个可变的关键字参数的字典 给函数实参(这些参数的数目未知,甚至长度可以为0)。

def test_kwargs(first, *args, **kwargs):print('Required argument: ', first)print(type(args))print(type(kwargs))for v in args:print ('Optional argument (args): ', v)for k, v in kwargs.items():print ('Optional argument %s (kwargs): %s' % (k, v))test_kwargs(1, 2, 3, 4, k1=5, k2=6)

  正如前面所说的,args 变量的类型是一个 tuple,而 kwargs 变量则是一个字典dict,并且args只能位于kwargs的前面
  test_kwargs 函数中,第一个参数 first 是形参,是必须要传入的参数,不带关键字的参数则作为可变参数列表传入函数实参,带关键字的参数则作为可变参数字典传入函数实参。所以test_kwargs(1, 2, 3, 4, k1=5, k2=6)中,1作为实参传入了 first;紧接着 2, 3, 4 三个数则作为可变参数列表传入实参,最后的 k1=5, k2=6 中的5,6作为可变参数字典传入实参。

运行结果如下:

Required argument:  1
<class 'tuple'>
<class 'dict'>
Optional argument (args):  2
Optional argument (args):  3
Optional argument (args):  4
Optional argument k2 (kwargs): 5
Optional argument k1 (kwargs): 6

3. 解包的应用

3.1 * 的应用

3.1.1 列表转元组巧妙方法

stars = ["黎明", "华仔", "郭富城", "张学友"]
# 方法一
print("四大天王:%s, %s, %s, %s" % (stars[0], stars[1], stars[2], stars[3]))
# 方法二
print("四大天王:%s, %s, %s, %s" % tuple(stars))
# 方法三
print("四大天王:%s, %s, %s, %s" % (*stars,))

  这三种方式输出结果一致,(*stars,)解包列表并转换了一个元组。

3.1.2 实现类似矩阵转置的操作

a = [[1, 2, 3], [4, 5, 6]]
for x, y in zip(*a):print(x, y)# 1 4
# 2 5
# 3 6

3.1.2 切分嵌套可迭代对象数据

>>> l = [('Bob', '1990-1-1', 60),
...     ('Mary', '1996-1-4', 50),
...     ('Nancy', '1993-3-1', 55)]
>>> for name, *args in l:
...     print(name, args)
...
Bob ['1990-1-1', 60]
Mary ['1996-1-4', 50]
Nancy ['1993-3-1', 55]

3.2 * 和 ** 的应用之装饰器

>>> def mydecorator(func):
...     def wrapper(*args, **kw):
...         print('I am using a decorator.')
...         return func(*args, **kw)
...     return wrapper
...
>>> @mydecorator
... def myfun(a, b):
...     print(a)
...     print(b)
...
>>> myfun(1, b = 2)
I am using a decorator.
1
2

  使用@定义myfun相当于myfun = mydecorator(myfun),定义出来的myfun其实是返回结果wrapper函数。
  wrapper函数使用*args, **kw作为参数,则被修饰的myfun函数需要的参数无论是什么样的,传入wrapper都不会报错,这保证了装饰器可以修饰各种各样函数的灵活性(毕竟我们一般在函数中传入参数时,要么所有参数名都写,要么前面几个不写,后面的会写,这样使用*args, **kw完全没有问题)。

4. 一些补充

4.1 函数传参时,可变参数(*)前不能指定参数名传参

def myfun(a, *b):print(a)print(b)
myfun(a=1,2,3,4)

报错如下:

    myfun(a=1,2,3,4)^
SyntaxError: positional argument follows keyword argument

正确的调用方法为:

>>> myfun(1,2,3,4)
1
(2, 3, 4)

4.2 设计调用函数时必须使用参数名传参

  一个函数想要使用时必须明确指定参数名进行传参,可以将这些参数放在可变参数之后。

def myfun(*, a, b):print(a)print(b)
myfun(a=1, b=2)

4.3 函数定义时,关键字参数只能作为最后一个参数

def myfun(a, *b, c, **d):print(a)print(b)print(c)print(d)
myfun(1, 2, w=6, c=3, d=4, e=5)     # 记住可变参数(*)之前不能指定参数名

运行结果如下:

1
(2,)
3
{'w': 6, 'd': 4, 'e': 5}

4.4 _ 与 * 赋值的区别

  • 一些元素不用时,采取分配临时名称“_”存着,可以让读代码的人知道这个元素是不要的,这种写法要求赋值时等号两侧的元素个数必须相等,即“_”相当于一个匿名变量,与普通变量一样只存储一个元素。
  • * 解包使得“变量数量 < 元素数量” 的赋值情况得以实现。

观察下例,“_” 与 “ * ” 的混合使用:

例①

>>> person = ('Bob', 20, 50, (11, 20, 2000))
>>> name, *_, (*_, year) = person
>>> name
'Bob'
>>> year
2000

例②

>>> l = [1,2,3,4,5,6,7,8,9,10,11,12]
>>> a,b,*_,c = l
>>> print(a,b,_,c)
1 2 [3, 4, 5, 6, 7, 8, 9, 10, 11] 12

参考
Python中的*args和**kwargs
Python中星号(*)的使用
Python的解包
python基础语法之拆包(解包)
python中的装包与解包*,**
python有趣的解包用法

Python的解包知识相关推荐

  1. python序列解包_python中解包

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 序列解包(sequence unpacking)是python中非常重要和常用的 ...

  2. 详解Python序列解包(5)

    如果一个函数需要以多种形式来接收参数,定义时一般把位置参数放在最前面,然后是默认值参数,接下来是一个星号的可变长度参数,最后是两个星号的可变长度参数:调用函数时,一般也按照这个顺序进行参数传递.调用函 ...

  3. 详解Python序列解包(4)

    本文主要介绍调用函数传递参数时序列解包的用法.在定义函数时在形参前面加2个星号**表示可变长度参数,可以收集若干关键参数形式的参数传递并存放到一个字典之中.与之相对,如果实参是个字典,可以使用两个星号 ...

  4. 详解Python序列解包(3)

    本文主要介绍调用函数传递参数时序列解包的用法.在调用函数传递参数时,可以在实参序列前加一个星号*进行序列解包,或在实参字典前加两个星号**进行解包,本文介绍第一种用法,第二种用法后面再单独发文介绍. ...

  5. python赋值语句(解包赋值、多目标赋值及共享引用)

    python赋值语句(解包赋值.多目标赋值及共享引用) 序列解包赋值语句,赋值一系列整数给一组变量 red,green,blue=range(3) red,blue #(0, 2) 元组赋值语句在循环 ...

  6. python序列解包求水仙花数_790.琉璃菜的糖浆欠火或过火,都会影响成品的( )。

    [填空题]kidney [填空题]管,隧道 [其它]1.随机产生20个1-100的整数, 并创建列表,用切片提取偶数位置的元素 2.随机产生5个元素( 0,1,2,3,4),然后对1,3元素进行4次方 ...

  7. Python * ** 打包解包详解

    里边出现的英文都来自于python官网的开发者指南 本文主要就是介绍*和**对此展开详细介绍. Unpacking is proposed to be allowed inside tuple, li ...

  8. python 链式比较 2._特色的Python序列解包、链式赋值、链式比较

    一.序列解包 序列解包(或可迭代对象解包):解包就是从序列中取出其中的元素的过程,将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中. 一般情况下要解包的序列包含的元素个数必须与你在等 ...

  9. python 序列解包(解压缩)

    所学的解压缩 如果我们给出一个列表,我们需要一次性取出多个值,我们是不是可以用下面的方式实现呢? name_list = ['nick', 'egon', 'jason'] x = name_list ...

最新文章

  1. Hashtable的使用
  2. Asp.NetCoreWebApi图片上传接口(二)集成IdentityServer4授权访问(附源码)
  3. linux kernel 调度,在Linux中,实时调度_kernel_开发99编程知识库
  4. python软件下载3版本-Python3.9下载
  5. 国足0:2不敌韩国 郑智:结果遗憾 对年轻球员是财富
  6. 是用int还是用Integer?
  7. 2020华为软挑热身赛
  8. 分享一下海康威视古老的播放器源码
  9. [Photography] 还是DPP好!
  10. python和excel进行数据交换_python-doc/使用Python和Excel进行交互式数据分析.md at master · HSUCHING/python-doc · GitHub...
  11. NRF52832学习笔记(34)——倾角传感器SCL3300使用
  12. “工程化”对于大型数据平台而言,意味着什么?StartDT Hackathon来了
  13. 计算机弹出虚拟U盘,我们通过工具所自带的电脑模拟器对我们u盘制作启动盘进行模拟启动测试...
  14. 恒压板框过滤实验数据处理_鞍山高温除尘袋公司,板框压滤机滤布,热门_泰翔工业滤料...
  15. 解决error: inlining failed in call to always_inline ‘int _mm_popcnt_u32(unsigned int)’
  16. Kinect深度图与摄像头RGB的标定与配准(文末总结不容错过)
  17. js的阻塞加载、延迟加载和异步加载
  18. 怎样使用CPropertyPage类和CPropertySheet类创建属性表
  19. 数据压缩第二周作业——图片TIFF格式分析
  20. 一个奇怪的bug,记录一下

热门文章

  1. Sublime Text 中文版安装和插件安装
  2. 省流版-38号车评中心历史车评文字汇总
  3. handlebars.js_Handlebars.js入门指南
  4. JOOQ 为table和view自动生成代码
  5. 复变函数不挂科——3小时学完复变函数与积分变换(猴博士复变函数学习笔记2)
  6. stm32串口通信最后一字节不对异常
  7. Boost在Windows XP运行的编译方法
  8. android百度日语输入法下载,百度日文输入法
  9. PyQt5 | PyQt5环境配置及组件介绍
  10. 弥补自己的无知: GPL与LGPL