原文地址: https://zhuanlan.zhihu.com/p/702419094
Packing 封包
所谓封包指的是,将多个值赋值给一个变量时,Python 会自动将这些值封包为一个元组
>>> count = 996, 123, -3
>>> count
(996, 123, -3)
Unpacking 解包
序列 / 可迭代对象解包
- 所谓 序列 / 可迭代对象解包 指的是:对一个序列(或可迭代对象)解包,并将得到的值存储到一系列变量中。
>>> name1, name2, name3 = ["Tom","Aaron","Bob"]
>>> print(f"{name1 = }")
>>> print(f"{name2 = }")
>>> print(f"{name3 = }")
name1 = 'Tom'
name2 = 'Aaron'
name3 = 'Bob'
- 解包的序列 (或可迭代对象) 包含的元素个数必须与等号左侧的变量数相同,否则会抛异常。
# ——————————————————————input——————————————————————
students = {"Bob", "Aaron", "tony"}
try:
s1, s2 = students
except Exception as ex:
print("ex:",ex)
try:
s1, s2, s3, s4 = students
except Exception as ex:
print("ex:",ex)
# ——————————————————————output——————————————————————
ex = ValueError('too many values to unpack (expected 2)')
ex = ValueError('not enough values to unpack (expected 4, got 3)')
- 如果解包的序列 (或可迭代对象) 包含的元素个数与等号左侧的变量数不同时,可在变量名前添加 * 星号,其会将多余的值以列表的形式赋值给该变量。
显然存在多个 * 星号变量会报错
>>> a, *b, c = (11, 22, 33, 44, 55, 66)
>>> print(f"{a = }")
>>> print(f"{b = }")
>>> print(f"{c = }")
a = 11
b = [22, 33, 44, 55]
c = 66
- 解包时如果不关心该位置的值, 可以使用占位符_下划线
>>> f, _, g, _ = (11, 22, 33, 44)
>>> print(f"{f = }")
>>> print(f"{g = }")
f = 11
g = 33
- 需要注意的是,进行解包时,左侧不能只有一个变量名(ps.这不废话么… 一个变量名还叫啥解包…这不就是直接赋值吗…😅)
>>> x = (11, 22, 33)
>>> print(f"{x = }")
x = (11, 22, 33)
# 此时,可以通过*_来实现
>>> y, *_ = (11, 22, 33)
>>> print(f"{y = }")
y = 11
解包传参
- 对于 序列对象,在实参添加 * 前缀,其会将序列对象解包为若干个元素,作为函数调用的位置实参
- 对于 字典对象,在实参添加 ** 前缀, 其会将字典解包为若干个键值对,作为函数调用的关键字实参
# ——————————————————————input——————————————————————
def fun(a,b,/,c,d,*args, x,y,z,**kw):
print("a:",a,"b:",b,"c:",c,"d:",d)
print("args:", args)
print("x:",x,"y:",y,"z:",z)
print("kw:", kw)
list1 = [0,1,2,3,4]
dict1 = {"x":5,"y":6,"z":7,"u":8,"v":9}
fun(*list1, **dict1)
# ——————————————————————output——————————————————————
a: 0 b: 1 c: 2 d: 3
args: (4,)
x: 5 y: 6 z: 7
kw: {'u': 8, 'v': 9}
交换变量
在其他语言中交换变量值,通常需要依赖中间变量来实现。但在 Python 中却可以直接交换。其中对于多变量交换时,其底层实现原理即是通过序列解包实现的
>>> a, b, c, d = 1, 2, 3, 4
>>> a, b, c, d = b, d, a, c
>>> print(f"{a = }")
>>> print(f"{b = }")
>>> print(f"{c = }")
>>> print(f"{d = }")
a = 2
b = 4
c = 1
d = 3
zip 函数
- zip 函数将可迭代对象作为参数,其会把各可迭代对象中对应位置的元素打包为一个元组。可将 zip 结果对象转换为 list 来查看、遍历。需要注意的是,当各可迭代对象的长度不同时,zip 函数将在长度最短的可迭代对象用完时,停止打包
# ——————————————————————input——————————————————————
names = ["Aaron", "Tom", "Bob", "Tony"]
ages = (18, 33, 72)
sexs = ["man", "male", "woman", "null"]
zip_result = zip(names, ages, sexs)
zip_list = list(zip_result)
print(f"{zip_result = }")
print(f"{zip_list = }")
# ——————————————————————input——————————————————————
zip_result = <zip object at 0x00000234AC4A1900>
zip_list = [('Aaron', 18, 'man'), ('Tom', 33, 'male'), ('Bob', 72, 'woman')]
- 对 zip 对象解包时,可在传递实参 zip 结果对象的前添加 * 前缀
[!important] zip 返回迭代器!!!
迭代器只能遍历一次!!!
如果对zip
结果输出遍历过一次,比如将其转换为了list
,则原本的zip
结果就不能再进行遍历了,也就不能再进行zip(*)
解包操作了,但是此时可以对转换得到的list
进行解包。
# ——————————————————————input——————————————————————
names = ["Aaron", "Tom", "Bob", "Tony"]
ages = (18, 33, 72)
sexs = ["man", "male", "woman", "null"]
zip_result = zip(names, ages, sexs)
zip_list = list(zip_result)
# 注意,zip_result在转换为list时已经迭代了一次了,所以无法再次迭代解包了
# my_name, my_age, my_sex = zip(*zip_result)
# 但是list可以多次迭代解包
my_name, my_age, my_sex = zip(*zip_list)
print(f"my_name : {my_name}")
print(f"my_age : {my_age}")
print(f"my_sex : {my_sex}")
# ——————————————————————output——————————————————————
my_name : ('Aaron', 'Tom', 'Bob')
my_age : (18, 33, 72)
my_sex : ('man', 'male', 'woman')
参考文献
- Python 编程 · 第 3 版:从入门到实践 Eric Matthes 著
- Python 基础教程 · 第 3 版 Magnus Lie Hetland 著
- 流畅的 Python· 第 1 版 Luciano Ramalho 著