在最初的「什么是变量」章节里我们简单学习了赋值语句,不知道你还记得多少事情发展到了今天,你我都有责……咳,你学完了分支和循环语句,学完了字符串、元组和列表,学完了集合和字典等;于是明白了什么是可变和不可变对象,什么是有序的可迭代对象,也明白了什么是无序集合什么是键值对——打个响指,是时候重新认识一下赋值语句了。
所谓赋值语句,就是将一个对象赋给一个名称。在赋值运算符等号的左侧是变量名称,右侧是对象。例如,以下代码将字符串对象'看见星光'赋值变量名StarNaked。
StarNaked = '看见星光'
然而这只是它最基础的表达方式……
1 丨 变量赋值的运算过程
先来复习一下变量赋值的运算过程和特点,可能大部分朋友已经忘记的差不多了。
StarNaked = '看见星光'
在第一次将对象赋值给变量时,Python会创建变量名,然后将其指向对象。例如,将字符串对象'看见星光'赋予变量名StarNaked,使StarNaked引用'看见星光'。此后,当这个变量名再次出现时,就会被它所引用的值取代。
以上这段话包含了2个知识点。第一个知识点是变量名在引用前必须先赋值,使用未赋值的变量名,Python不知道它所引用的值是啥,就会抛出异常——Python和其它编程语言不同,变量不存在默认值。
例如,以下代码中变量n未先赋值,代码运行后抛出异常:name 'n' is not defined
while n<3:
n+=1
print(n)
解决方法是先对变量n赋值。
n = 0 #变量赋值初始化
while n<3:
n+=1
print(n)
另外一个知识点是变量总是引用(指向)对象,而不是复制(储存)对象。换句话说,Python的变量像是一个指针,指向对象,而不像其它编程语言,变量是一个盒子,把对象直接储存其中——如果你忘记了这段话是啥意思,建议重读「什么是Python变量」
2 丨 赋值语句
在大部分情况下,赋值语句都是简单的——摊手,这句话的意思是在小部分情况下会比较特殊。
除了基本形式的赋值语句之外,常用的赋值语句形式还有链式赋值、增强赋值、海象赋值、序列赋值与解包等。
2.1 链式赋值
使用链式赋值可以将多个变量名赋予相同值。例如,以下代码将变量a/b/c都赋值字符串'看见星光'。
a = b = c = '看见星光'
上文我们讲过,Python的变量是引用,使用链式赋值时,等于多个变量引用了同一个对象。当这个对象是不可变类型时,没有问题,任意变量内容的修改不会影响到其它变量,所以以上代码等同于以下代码:
a = '看见星光'
b = '看见星光'
c = '看见星光'
但当链式对象为可变类型(列表、集合、字典等)时,你就需要格外小心了🤣。任意一个变量内容的修改都会影响到其它变量。
a = b = c = ['看见星光']
a.append('公众号Excel星球')
print(b,c)
以上代码中第2行代码为列表a新增一个元素。第3行代码打印其余两个变量b和c,打印结果显示这俩列表也都和列表a一样发生了变动,内容被修改为:
['看见星光', '公众号Excel星球']
因此以上代码不等于以下代码:
a = ['看见星光']
b = ['看见星光']
c = ['看见星光']
a.append('公众号Excel星球')
print(b,c)
2.2 增强赋值
k+=1这种变量赋值方式被称为增强赋值,又或者内联赋值,它等同于k=k+1。两者在计算效率上的差别微乎其微,但前者更简洁也更优雅,但使用哪种方式取决于你的个人习惯——别看我,看我我就看着你,嗯,那个你还是用k+=1吧~
2.3 海象运算符
海象运算符是Python 3.8版本引入的一个新的特性,名称来源于其形状像海象的牙齿。嘿嘿👇。它允许在表达式内部进行赋值操作,而不需要单独一行代码来完成赋值。具体表达方式为 变量:=内容 。
举个例子。
以下是「什么是集合」教程里一道课后练习题。
如上图所示,A~H列是明细,记录了每个人参加过的球类项目,现在需要统计每个人不重复的项目数量和明细,模拟结果见I列。
一行列表推导式的解法参考如下:
代码看不全可以左右拖动...▼
[
[f'共有{len(s:=set("/".join(filter(None,row)).split("/")))}项,分别是:{"-".join(s)}']
for row in arr
]
代码使用了海象运算符,将每行记录中的项目构建成一个集合,赋值变量s。
s:=set("/".join(filter(None,row)).split("/")))
2.4 序列赋值与解包
序列赋值是指将一个有序的可迭代对象(字符串/元组/列表等)的元素分配给多个变量,这种赋值方式又被称为解包,它允许我们一次性为多个变量赋值,只要左边的变量名称的数量与右边可迭代对象中的元素数量相匹配即可。
以下是一个元组序列赋值(元组解包):
boy,girl = '看见星光','王小睿'
print(boy,girl)
第1行代码等号的左右都是元组结构,省略了括号,系统会自动打包成元组,它等同于以下代码:
(boy,girl) = ('看见星光','王小睿')
print(boy,girl)
以下是一个列表序列赋值(列表解包):
boy,girl = ['看见星光','王小睿']
print(boy,girl)
以上赋值语句的计算过程是,Python按照序列位置将右边的对象和左边的目标从左到右依次配对。例如,先将第1个对象看见星光配给boy,再将第2个对象王小睿配给girl。
打个响指,说一个看似特殊的情况。猜一猜,以下代码返回的结果是什么?
boy,girl = '看见星光','王小睿'
boy,girl = girl,boy
print(boy,girl)
第1行代码是元组解包,将变量boy,girl分别赋值为看见星光和王小睿。
第2行代码还是一个元组解包,它等同于boy,girl = (girl,boy),表示将变量boy,girl分别赋值为王小睿(girl的值)和看见星光(boy的值)。
所以最后打印的结果内容boy是王小睿,girl是看见星光——这个技巧常用于多个变量之间的交换。
……
当赋值运算符两边的数量不一致时,系统会抛出异常:too many values to unpack
boy,others = ['看见星光','王小睿','星辰']
print(boy,others)
以上代码左边有2个目标,右边的列表有3个元素,男多女少,值多变量名少,于是too many values。
如果需要将列表的第1个元素赋值变量名boy,剩余的元素赋值变量名others,你可能会立刻想起列表切片,真是个聪明的人类:
arr = ['看见星光','王小睿','星辰']
boy,others = arr[0],arr[1:]
print(boy,others)
不过更简洁更优美的方式依然是使用解包:
boy,*others = ['看见星光','王小睿','星辰']
print(boy,others)
以上代码,左侧第2个变量名称前面有一个醒目的星号*,你可以把它理解为通配符,它表示将已经明确匹配的序列元素扣除,再将剩下的元素打包成列表统统扔给它。因此这段代码表示将第1个元素赋值变量boy,剩下的元素都以列表的形式赋予others。第2行代码打印结果为 '看见星光' ['王小睿','星辰']
同样的道理,以下代码将第1个和最后1个元素赋值给变量boy和girl,剩余的元素以列表的形式赋值给变量others。代码最后打印结果为'看见星光' ['星辰','掌门'] '王小睿'
boy,*others,girl = ['看见星光','星辰','掌门','王小睿']
print(boy,others,girl)
……
需要注意的是,带星号的名称赋值内容必然是一个列表,不论赋值结果是多个值,还是单个值,甚至没有值。
以下代码others被赋值为单个元素的列表['星辰']
boy,*others,girl = ['看见星光','星辰','王小睿']
print(boy,others,girl)
以下代码左侧有3个变量名称,右侧列表只有2个元素,以至于others一无所有,被赋值为空列表[]
boy,girl,*others, = ['看见星光','王小睿']
print(boy,girl,others)
#'看见星光' '王小睿' []
2.5 字典解包
除了以上举例的有序可迭代对象(元组和列表)的解包之外,字典解包也是一种常见的形式,它的语法格式为 **dict。常用于字典合并和函数传参。
以下代码将两个字典合并成一个(这里可以思考一下和update()方法的异同):
dict1 = {'姓名':'看见星光','得分':18}
dict2 = {'姓名':'看见星光','性别':'男'}
dict3 = {**dict1,**dict2}
print(dict3)
#{'姓名': '看见星光', '得分': 18, '性别': '男'}
以下代码将字典解包作为函数的参数。
def func(name,sex,country='中国'):
return f'这个名为"{name}"的人是{sex}的,{country}人'
dict1 = {'name':'看见星光','sex':'男'}
print(func(**dict1))
#这个名为"看见星光"的人是男的,中国人
解包后字典的每个键视为函数参数的名字,例如代码中键name对应参数name,而键对应的值则作为参数的值,例如键name对应的值为看见星光,则函数参数name传入的值也为看见星光——言下之意,字典的键和函数的参数名必须保持一致。
2.6 解包在循环语句中的应用
在列表迭代中使用解包是最常见的场景之一,我举一个之前在「字典」教程中应用过的小例子。
本期案例工作簿下载:www.www.com
工作簿内有一个名称为奖学金的工作表,内容如下图所示。A~D列是数据源,需要按学校和班级分类汇总统计奖学金总额,F~H列为模拟结果。
参考代码如下:
lst = sht.range('a2:d2').expand('down').value
dict = {}
for sch,_,cls,bursary in lst:
dict[sch,cls] = dict.setdefault((sch,cls),0) + bursary
aRes = [key + (value,) for key,value in dict.items()]
sht.range('f1').value = [['学校','班级','奖学金']] + aRes
第3~4行代码迭代数据源列表lst,使用字典分类累加奖学金。其中第3行代码使用列表了解包,将迭代中每行子列表的4个元素按序列分别赋值sch,_,cls,bursary。第4行代码将学校sch和班级cls作为key,累加对应的奖学金。
……
打个响指,突然就没了,想起什么再补。有什么问题照例可以在VIP会员微信答疑群中提问交流,挥挥手,下期再见。
……
🔽点击阅读原文
从0到1系统学习Python