python把a当作b_Python中的zip(), *zip()与zip(*zip(a,b))

作为一个Python初学者,昨天在实践书上一个实例时遇到了zip()和*zip()函数,而书中恰好对这两个函数没有过多的解释(这两个函数其实是同一个函数),网上搜索后对zip()倒是弄明白了,但是对*zip()的说法,CSDN上大部分人却是说其是zip()的逆操作云云,但是操作下来感觉完全不是一回事,后来又搜了几个不同的文章,结合自己的代码操作后才完全把这个事情弄明白,所以将其记录下来,方便自己的同时也看看能不能帮助到同样有疑惑的Python初学者们。

首先说结论:

1. zip()和*zip()是同一个函数,而非后者是前者的逆操作,两者操作结果不同的原因主要是因为星号*的存在

2. 通俗的说,倒不如说zip()是压缩操作,而*星号是解压缩操作,* 才是zip()的逆操作

下面简述zip()函数并用coding来说明我的结论:

Zip():

作用:压缩数组,把多个列表压缩成多个多元元素(ps.这里的几元元素指的是一个元素里有几个值,如果我的说法不准确或者错用了名词,欢迎大家指正我)

特性:

1. 当操作数之间长度不一致时,其长度与最短操作数的长度一致

a = [1,2,3]

b = [4,5,6,7]

c = [4,5,6,7,8]

zipped = zip(a,b,c)

print(*zipped) = (1, 4, 4) (2, 5, 5) (3, 6, 6)

a, b, c的长度分别为3,4,5,最后zipped的长度为3,把三个列表压缩成了3个三元元素,(为什么这里输出时要加个*号呢,请大家往下看)

2. 在python3中,zip()函数返回的值是一个迭代器,这个迭代器表示的是数据存储在内存中的哪个位置,而不能直接进行访问/输出/使用,需要用list,for循环或者*号解压后才能正常显示。

print(zipped)

print(list(zipped))

print(*list(zipped))

#输出分别为

[(1, 4, 4), (2, 5, 5), (3, 6, 6)]

(1, 4, 4) (2, 5, 5) (3, 6, 6)

此时我们分别对比输出结果,反应快的朋友可能已经发现了,*号的存在使得输出结果“少了一层”,从一个由“三个元组组成的列表”变成了“三个元组”(ps.元组就是组内元素不可改变的列表)。

所以也可以说,*号让一个二维列表变成了多个一维元组。所以当我简单的print(*zipped)时,星号的作用是把zipped的“迭代器”格式解了一层,把它变成了可以打印出来的“数据格式”,接下来我会在下面用coding再次简述一下*号。

注意:有时候你可能会发现,你输出完zipped后,想再次输出时,列表里为空了。这是因为,zip()在python3中返回一个迭代器,你只能在迭代器迭代一次,用完后元素虽然不会消失,但再次迭代会得到0个元素,除zip外,map,filter也是如此,因为python就是这么规定的。

*号的特性:对操作数进行“解压“

a = 5

b = [3,4,5]

c = [(3,4,5), (6,7,8)]

d = [[(3,4,5),(6,7,8)]]

---

print(*a) #结果会出现error

print(*b)

print(*c)

print(*d)

#输出分别为

Error

3 4 5

(3, 4, 5) (6, 7, 8)

[(3, 4, 5), (6, 7, 8)]

可以发现,*号把所有操作数都解压了一层,二维列表变成了一维,三维变成了二维,而对int进行解压报错时,错误提示为

print() argument after * must be an iterable, not int

*号后必须为迭代器,而不能是int类型的值

可以简单理解为int已经是“数据基础格式”了,不能再进行解压缩了

简述完zip()和*号,接下来就是最后的zip(*zip(a,b))及利用zip进行矩阵运算。

很多人说*zip()是zip()的逆操作,但是当他们展示code时,却往往写的是zip(*zip(a,b))(CSDN上超过一半的人对zip(), *zip()都是这样说的,直至我搜关于*星号的文章后才恍然大悟)按照这个逻辑,其实应该是*zip(zip(a,b))才对,因为根据函数嵌套,总是先运行括号内的,zip(*zip(a,b))不就变成“先解压缩再进行压缩”了?

然而,*zip(zip(a,b))后,输出的结果并不是预想中的结果,反而m,n = zip(*zip(a,b))输出的结果才是原来的结果,这是为什么呢?

1. *zip(zip(a,b))

首先说一下,当zip后只有一个列表时,他会进行迭代操作,把这个列表和元素为0的空列表进行迭代,所以当zip的操作数只有一个时,程序并不会报错。

a = [1,2,3]

zipped = zip(a)

print(list(zipped)) = [(1,), (2,), (3,)]

a = [1,2,3]

b = [4,5,6,7]

zipped = zip(a,b) #此时zipped中数据为(1,4) (2,5) (3,6)

zippeded = zip(zipped) #再调用一次zip

print(*zippeded) = ((1, 4),) ((2, 5),) ((3, 6),) #简直和原来的a,b大相径庭

2. m,n = zip(*zip(a,b))

m = [1, 2, 3]

n = [4, 5, 6]

print("zip(m, n)返回:", list(zip(m, n)))

print("*zip(m, n)返回:", *zip(m, n))

m2, n2 = zip(*zip(m, n))

print("m2和n2的值分别为:", m2, n2)

#输出结果为

zip(m, n)返回: [(1, 4), (2, 5), (3, 6)]

*zip(m, n)返回: (1, 4) (2, 5) (3, 6)

m2和n2的值分别为: (1, 2, 3) (4, 5, 6)

虽然括号不一样了,从一开始的[ ]变成了后面的( ),但是既然元组是元素不可变的列表,两者的值其实都是一样的,进一步拆解一下:

#因为*zip(m,n)返回值为(1,4) (2,5) (3,6),此时再对其zip一遍,是不是等效于下面的代码了

_1 = (1,4)

_2 = (2,5)

_3 = (3,6)

m1,m2 = zip(_1,_2,_3) #分别把结果的2个元素赋给m1和m2,所以又还原成了(1,2,3) (4,5,6)

所以,并不是*zip()是逆操作,而是又进行了一次zip()操作,把列表转置回来了。

因此,zip()也可用于进行矩阵运算,当你进行一次zip时,横坐标和纵坐标调换了一次,这在矩阵乘法中很常见,而使用2次zip操作,则又把横纵坐标调换回来了,或许这也是各位CSDN作者只从结果看,又看到zip()和*zip()这两个看起来如此对仗的操作,便把两者当成互逆操作的原因吧,其实*在其中的作用只是解压了第一次zip后的迭代器格式,把其转换成数据格式罢了。

带*号变量严格来说并不是一个变量,在解压后,它其实是一个参数,一个数据,它不能作为一个变量而被赋值,但可以作为参数传递。正如你不能对int数1,2,3,4,5赋值一样

洋洋洒洒写了不少,希望能帮助到大家,如果我的文章中有什么错误或者不准确的地方,也欢迎大家勘误,大家共同学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值