在Python中,列表的赋值操作有两种常见形式,理解它们的区别对掌握数据结构的行为至关重要。
第一种是直接将一个列表赋值给另一个变量。这种情况下,两个变量实际上指向内存中的同一个对象。这意味着对其中一个列表的修改会直接影响另一个,即“一改全改”。
第二种方式是将原列表的副本赋给新变量。此时,两个变量分别指向不同的对象,彼此独立,互不干扰。
clist1 = list("oeasy")
clist2 = clist1
clist2 = clist1.copy()
接下来探讨列表是否支持加法运算。
回顾字符串的操作:字符串支持加法(拼接)和乘法(重复),那么列表是否也具备类似特性?
lst1 + lst2
"动词打次" + "东东打次"
"动词打次" * 3 + "东东打次"
("动词打次" * 3 + "东东打次") * 4
以字符串为例,加法可以用于组合文本内容,例如将四大名著名称进行拼接,形成一个新的字符串序列。
import random
# 从四大名著中分别提取角色和事迹
# 《西游记》
journey_to_the_west_characters = ["孙悟空", "唐僧", "沙僧"]
journey_to_the_west_stories = ["大闹天宫", "三打白骨精", "真假美猴王"]
# 《红楼梦》
dream_of_the_red_chamber_characters = ["林黛玉", "贾宝玉", "薛宝钗"]
dream_of_the_red_chamber_stories = ["黛玉葬花", "宝玉挨打", "宝钗扑蝶"]
# 《三国演义》
romance_of_the_three_kingdoms_characters = ["刘备", "关羽", "诸葛亮"]
romance_of_the_three_kingdoms_stories = ["桃园结义", "草船借箭", "空城计"]
# 《水浒传》
water_margin_characters = ["宋江", "武松", "鲁智深"]
water_margin_stories = ["怒杀阎婆惜", "景阳冈打虎", "倒拔垂杨柳"]
# 汇总角色列表
all_characters = journey_to_the_west_characters + \
dream_of_the_red_chamber_characters + \
romance_of_the_three_kingdoms_characters + \
water_margin_characters
# 汇总事迹列表
all_stories = journey_to_the_west_stories + \
dream_of_the_red_chamber_stories + \
romance_of_the_three_kingdoms_stories + \
water_margin_stories
# 随机搭配角色和事迹
random_character = random.choice(all_characters)
random_story = random.choice(all_stories)
print(f"{random_character}——{random_story}")
执行后的结果如下所示:
代码中出现的反斜杠符号 \ 是续行符(line-continuation character),它的作用是让一行代码跨越多行书写,避免过长的横向滚动,提升可读性。
# 汇总角色列表
all_characters = journey_to_the_west_characters + \
dream_of_the_red_chamber_characters + \
romance_of_the_three_kingdoms_characters + \
water_margin_characters
对于数字列表,同样可以使用加法运算符进行合并。
通过在线可视化工具(如 Python Tutor)可以清晰地观察这一过程:
https://pythontutor.com/render.html#mode=display
lst1 = list(range(3))
print("lst1:", lst1)
lst2 = [3, 4, 5]
print("lst2:", lst2)
print("lst1 + lst2:", lst1 + lst2)
数字列表之间可以直接相加,生成一个新的列表。
同理,字符串列表也可以进行加法操作。
加法运算允许我们将两个字符串列表连接成一个更长的列表。
lst1 = list("oeasy")
lst2 = list("o2z")
print(lst1 + lst2)
然而,如果只是执行加法而不将结果赋值给任何变量,该结果会立即被丢弃。
没
这种情况下的结果会很快消失。
如何避免结果丢失呢?
解决方法是将相加的结果显式赋值给一个变量,例如 lst3。
没
lst1 = list("oeasy")
lst2 = list("o2z")
lst3 = lst1 + lst2
一旦结果被变量引用,就不会被系统清除。
垃圾回收
这背后涉及的是Python的垃圾回收机制。
什么是垃圾回收?
垃圾回收
在C语言中,程序员需要手动分配和释放内存空间。虽然分配容易,但常常因忘记释放而导致内存占用不断增长,最终引发内存泄漏甚至程序崩溃。
而像Python这样的现代语言,其解释器内置了自动垃圾回收机制。系统会定期检查已分配的内存块是否还有变量在引用它。
可以通过 getrefcount 函数查看对象的引用计数。当引用计数归零时,该内存空间就会被自动回收,这一过程称为垃圾回收(garbage collection)。
lst3 = lst1 + lst2
因此,将列表相加的结果赋给 lst3 后,该结果就能被保留下来。
如果将相加结果直接赋回给原变量 lst1 呢?
即将列表之和重新赋值给 lst1,操作是可以成功的。
lst1 = list("oeasy")
lst2 = list("o2z")
lst1 = lst1 + lst2
那么,能否使用增强赋值运算符呢?
增强赋值是一种特殊的赋值方式,在赋值的同时还包含某种“增强”操作。例如 += 就是典型的增强赋值运算符(augmented assignment)。
lst1 = list("oeasy")
lst2 = list("o2z")
lst1 += lst2
表达式 lst1 += lst2 在效果上等价于 lst1 = lst1 + lst2。
这两者完全一样吗?
从最终结果来看,两者表现一致。
lst1 = list("oeasy")
print("lst1:", id(lst1))
lst2 = list("o2z")
print("lst2:", id(lst2))
lst1 += lst2
print("lst1:", id(lst1))
但从内存地址的角度观察,可以发现差异:使用 += 时,lst1 的内存地址保持不变。
而采用先求和再赋值的方式(l1 = l1 + l2),l1 将指向一个新的内存地址。
lst1 = list("oeasy")
print("lst1:", id(lst1))
lst2 = list("o2z")
print("lst2:", id(lst2))
lst1 = lst1 + lst2
print("lst1:", id(lst1))
这种区别有何影响?
虽然效果相同,但效率不同!
普通加法运算效率较低,因为它需要创建一个全新的列表,然后依次复制原列表中的所有元素,再追加第二个列表的内容,最后完成赋值。
相比之下,增强赋值更加高效。
增强赋值本质上是通过 extend 方法来扩展原列表,而不是创建新对象。
extend 正是列表的一个成员方法,专门用于将一个列表的元素逐个添加到另一个列表末尾。
lst1 = list("oeasy")
lst2 = list("o2z")
lst1.extend(lst2)
它能够真正实现“扩列”——即扩展原始列表。
那么 extend 方法具体如何使用?可以通过 help() 查询其帮助文档了解用法。
help(list.extend)
extend 方法会遍历传入的参数列表,并将其每个元素依次添加到调用该方法的列表末尾。
总结一下,将两个列表相加有多种方式:
- 使用 lst1 = lst1 + lst2:生成一个位于新地址的新列表。
- 使用 lst1 += lst2
- 使用 lst1[-1:] = lst2
- 使用 lst1.extend(lst2)
后三种方式都属于就地修改(in-place),不会改变原列表的内存地址,效率更高。
- lst1 = lst1 + lst2
- 加法的本质 是 将两个列表 拼接
- 并将 结果位置 赋给帧(frame)上的变量 来引用
对比 extend 和 append 方法也很重要。
区别
它们的主要区别在于处理方式和效果。
num_list = [1, 2, 3]
print(num_list)
num_list.append([4, 5])
print(num_list)
num_list.remove([4, 5])
print(num_list)
num_list.extend([4, 5])
print(num_list)
append 方法是将整个对象作为一个元素添加到列表末尾,即使传入的是一个列表,也会被当作单个元素加入。
而 extend 方法则是将新列表中的每一个元素逐一取出,并追加到原列表的末尾,实现真正的列表合并。
这种差异类似于加法赋值与增强赋值之间的对比。
| 对比项 | append | extend |
|---|---|---|
| 描述 | 添加的是列表项 | 扩展原始列表 |
| 特点 | 将元素作为整体添加到列表末尾 | 把新列表对接到原列表尾巴上,合并两个列表 |
综上所述,我们学习了列表加法的基本用法以及如何处理相加结果。
扩充列表主要有三种高效方式:
- lst1 += lst2
- lst1[-1:] = lst2
- lst1.extend(lst2)
这三者在功能上相似,都能实现列表合并且不产生新地址。
地址
与普通加法相比,上述方法不会改变 l1 的内存地址。
效率
更重要的是,它们效率更高,因为是直接在原列表基础上进行扩展,无需创建新对象。
既然存在列表的加法操作,
加法
那是否存在列表的减法操作呢?
乘法
这个问题我们留待下次讨论。


雷达卡


京公网安备 11010802022788号







