python散装笔记——141: 列表解构(也称为打包和解包)
1: 解构赋值
在赋值中,可以使用“解包”语法将可迭代对象拆分为多个值:
解构为值
a, b = (1, 2)
print(a)
# Prints: 1
print(b)
# Prints: 2
如果尝试解包的元素数量多于可迭代对象的长度,将会引发错误:
a, b, c = [1]
# Raises: ValueError: not enough values to unpack (expected 3, got 1)
解构为列表
可以使用以下语法解包未知长度的列表:
head, *tail = [1, 2, 3, 4, 5]
这里,我们将第一个值提取为标量,其他值提取为列表:
print(head)
# Prints: 1
print(tail)
# Prints: [2, 3, 4, 5]
这相当于:
l = [1, 2, 3, 4, 5]
head = l[0]
tail = l[1:]
它也适用于多个元素或列表末尾的元素:
a, b, *other, z = [1, 2, 3, 4, 5]
print(a, b, z, other)
# Prints: 1 2 5 [3, 4]
在解构赋值中忽略值
如果只对给定的值感兴趣,可以使用_来表示你对其他值不感兴趣。注意:这仍然会设置_,只是大多数人不会将其用作变量。
a, _ = [1, 2]
print(a)
# Prints: 1
a, _, c = (1, 2, 3)
print(a)
# Prints: 1
print(c)
# Prints: 3
在解构赋值中忽略多个值
最后,可以使用_*语法在赋值中忽略多个值:
a, *_ = [1, 2, 3, 4, 5]
print(a)
# Prints: 1
这本身并不特别有趣,因为你可以直接使用列表索引来实现。真正有趣的是在一次赋值中保留第一个和最后一个值:
a, *_, b = [1, 2, 3, 4, 5]
print(a, b)
# Prints: 1 5
或者一次性提取多个值:
a, _, b, _, c, *_ = [1, 2, 3, 4, 5, 6]
print(a, b, c)
# Prints: 1 3 5
2: 打包函数参数
在函数中,可以定义一些强制性参数:
def fun1(arg1, arg2, arg3):
return (arg1,arg2,arg3)
这将使函数仅在提供三个参数时可调用:
fun1(1, 2, 3)
你也可以通过使用默认值将参数定义为可选的:
def fun2(arg1='a', arg2='b', arg3='c'):
return (arg1,arg2,arg3)
这样你可以以多种方式调用函数,例如:
fun2(1) # 返回: (1,b,c)
fun2(1, 2) # 返回: (1,2,c)
fun2(arg2=2, arg3=3) # 返回: (a,2,3)
...
但你也可以使用解构语法打包参数,以便使用列表或dict分配变量。
打包列表参数
假设你有一个值的列表:
l = [1,2,3]
你可以使用*语法将列表作为参数传递给函数:
fun1(*l)
# 返回: (1,2,3)
fun1(*['w', 't', 'f'])
# 返回: ('w','t','f')
但如果提供的列表长度与参数数量不匹配:
fun1(*['oops'])
# 抛出异常 TypeError: fun1() missing 2 required positional arguments: 'arg2' and 'arg3'
打包关键字参数
现在,你也可以使用字典打包参数。你可以使用**运算符告诉Python将dict解包为参数值:
d = {
'arg1': 1,
'arg2': 2,
'arg3': 3
}
fun1(**d)
# 返回: (1, 2, 3)
当函数只有位置参数(没有默认值的参数)时,你需要字典包含所有预期的参数,且不能有多余的参数,否则会报错:
fun1(**{'arg1':1, 'arg2':2})
# 抛出异常: TypeError: fun1() missing 1 required positional argument: 'arg3'
fun1(**{'arg1':1, 'arg2':2, 'arg3':3, 'arg4':4})
# 抛出异常: TypeError: fun1() got an unexpected keyword argument 'arg4'
对于具有可选参数的函数,你可以以相同的方式将参数打包为字典:
fun2(**d)
# 返回: (1, 2, 3)
但你可以省略一些值,因为它们将被默认值替换:
fun2(**{'arg2': 2})
# 返回: ('a', 2, 'c')
和之前一样,你不能提供不存在的参数的额外值:
fun2(**{'arg1':1, 'arg2':2, 'arg3':3, 'arg4':4})
# 抛出异常: TypeError: fun2() got an unexpected keyword argument 'arg4'
在实际使用中,函数可以同时具有位置参数和可选参数,其工作方式相同:
def fun3(arg1, arg2='b', arg3='c'):
return (arg1, arg2, arg3)
你可以只用一个可迭代对象调用函数:
fun3(*[1])
# 返回: (1, 'b', 'c')
fun3(*[1,2,3])
# 返回: (1, 2, 3)
或者只用一个字典:
fun3(**{'arg1':1})
# 返回: (1, 'b', 'c')
fun3(**{'arg1':1, 'arg2':2, 'arg3':3})
# 返回: (1, 2, 3)
或者在同一个调用中同时使用两者:
fun3(*[1,2], **{'arg3':3})
# 返回: (1,2,3)
但要注意,你不能为同一个参数提供多个值:
fun3(*[1,2], **{'arg2':42, 'arg3':3})
# 抛出异常: TypeError: fun3() got multiple values for argument 'arg2'
3: 解包函数参数
当您希望创建一个可以接受任意数量参数的函数,并且不在“编译”时强制参数的位置或名称时,这是可能的,方法如下:
def fun1(*args, **kwargs):
print(args, kwargs)
*args和**kwargs参数是特殊的参数,分别设置为一个tuple和一个dict:
fun1(1,2,3)
# Prints: (1, 2, 3) {}
fun1(a=1, b=2, c=3)
# Prints: () {'a': 1, 'b': 2, 'c': 3}
fun1('x', 'y', 'z', a=1, b=2, c=3)
# Prints: ('x', 'y', 'z') {'a': 1, 'b': 2, 'c': 3}
如果你查看足够多的Python代码,你会很快发现它被广泛用于将参数传递给另一个函数。例如,如果你想扩展字符串类:
class MyString(str):
def __init__(self, *args, **kwarg):
print('Constructing MyString')
super(MyString, self).__init__(*args, **kwarg)