Python

python解包 --*args **kwargs

2018-09-22  本文已影响26人  cook__

任何可迭代对象都支持解包,可迭代对象包括元组、字典、集合、字符串、生成器等实现了_next__方法的一切对象。
列表:

>>> 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 = "abc"
>>> a
'a'
>>> b
'b'
>>> c
'c'

字典:

>>> a,b,c = {"a":1, "b":2, "c":3}
>>> a
'a'
>>> b
'b'
>>> c
'c'
字典解包后,只会把字典的 key 取出来,value 则丢掉了。

解包应用实例:-----多变量赋值, 本质上也是自动解包过程

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

所以写代码的时候需要特别注意。在 Python 中,交换两个变量非常方便,本质上也是自动解包过程。

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

使用"*表达式"来分解任意长度的可迭代对象中的元素,返回list对象

在 Python2 中,如果等号左边变量的个数不等于右边可迭代对象中元素的个数,是不允许解包的。但在 Python3 可以这么做了。

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

这种语法就是在某个变量面前加一个星号,而且这个星号可以放在任意变量,每个变量都分配一个元素后,剩下的元素都分配给这个带星号的变量。

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

*表达式语法使得你的代码写起来更简洁,比如上面例子,在 Python2 中该怎么操作呢?

>>> n = [1,2,3,4]
# 使用切片操作
>>> a, b, c = n[0], n[1:-1], n[-1]
>>> a
1
>>> b
[2, 3]
>>> c
4

应用实例:
1、迭代变长的元组序列

records = [
    ('foo', 1, 2),
    ('bar', 'hello'),
    ('foo', 3, 4)
]

def do_foo(x, y):
    print('foo', x, y)

def do_bar(s):
    print('bar', s)

for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)

2、与字符串处理操作结合

line = 'nobody:*"-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
uname, *fields, homedir, sh = line.split(':')
uname
Out[7]: 'nobody'
homedir
Out[8]: '/var/empty'
sh
Out[9]: '/usr/bin/false'

函数调用时的解包操作:函数调用时,有时你可能会用到两个符号:星号* 和 双星号**。
>>> def func(a,b,c):
...     print(a,b,c)
>>> func(1,2,3)
1 2 3

func 函数定义了三个位置参数 a,b,c,调用该函数必须传入三个参数,除此之外,你也可以传入包含有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

函数被调用的时候,使用星号 * 解包一个可迭代对象作为函数的参数。字典对象,可以使用两个星号,解包之后将作为关键字参数传递给函数

>>> func(**{"a":1,"b":2,"c":3})
1 2 3
** 符号作用的对象是字典对象,它会自动解包成关键字参数 key=value 的格式:
>>> 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
>>>

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

Python 3.4 中 print 函数 不允许多个 * 操作
>>> print(*[1,2,3], *[3,4])
  File "<stdin>", line 1
    print(*[1,2,3], *[3,4])
                    ^
SyntaxError: invalid syntax
>>>
再来看看 python3.5以上版本,可以使用任意多个解包操作
>>> print(*[1], *[2], 3)
1 2 3

从 3.5 开始可以接受多个解包,于此同时,解包操作除了用在函数调用,还可以作用在表达式中。

>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}

新的语法使得我们的代码更加优雅了,例如拼接两个列表可以这样:

>>> list1 = [1,2,3]
>>> list2 = range(3,6)
>>> [*list1, *list2]
[1, 2, 3, 3, 4, 5]
>>>

可不可以直接用 + 操作呢?不行,因为 list 类型无法与 range 对象相加,你必须先将 list2 强制转换为 list 对象才能做 +操作。

再来看一个例子:如何优雅的合并两个字典

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

在3.5之前的版本,你不得不写更多的代码:

>>> import copy
>>>
>>> c = copy.deepcopy(a)
>>> c.update(b)
>>> c
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

最后总结一下:

1、自动解包支持一切可迭代对象
2、python3中,支持更高级的解包操作,用星号操作使得等号左边的变量个数可以少于右边迭代对象中元素的个数。
3、函数调用时,可以用 * 或者 ** 解包可迭代对象,作为参数传递
python3.5,函数调用和表达式中可支持更多的解包操作。

上一篇下一篇

猜你喜欢

热点阅读