列表 list 和元组 tuple 基本使用
列表
- 可变
- [] 表示
- 线性的数据结构
- 列表内元素有顺序,可用索引
- 列表内元素可以是任意对象(数字,字符串,对象,列表等)
list 定义及初始化
- list()
- list(iterable)
- 不能一开始就定义大小,区别 c 中的数组
lst = list()
lst = []
lst = [1,'a','ab',[1,2],(1,),{1,2},{'a':1}]
lst = list[range(4)]
list 索引访问
- 索引:正索引 0 开始,负索引 - 1 开始,不可越界,否则抛出 IndexError 异常
- list[index]
list 查询函数
- index(value[,start][,stop])
- 在 start 到 stop 区间内查找 value 值
- 匹配到第一个立即返回索引
- 匹配不到,抛出 ValueError 异常
- 时间复杂度 O(n)
- count(value)
- 返回列表中匹配 value 的次数
- 时间复杂度 O(n)
- len()
- 返回列表长度
list 元素修改
- 索引访问修改
- list[index] = value
- 索引不要越界
list 增加、插入元素
- append(object)
- 返回值为 None
- 无新的列表产生,修改原列表
- 时间复杂度 O(1)
- insert(index,object)
- 在指定索引处插入元素 object
- 返回 None
- 无新的列表产生,修改原列表
- 时间复杂度 O(n)
- 索引可超界:超上界,尾部追加,超下界,首部追加
- extend(iterable)
- 将可迭代对象追加进来,返回 None
- 无新的列表产生,修改原列表
+
- 连接两个 list 生成新的 list,原 list 不变
- 本质是调用了魔术方法
__add__
*
- 重复操作,将本列表元素重复 n 次,返回新的列表
# 比较
x = [[1,2]]*2
print(x)
x[0][0] = 100
print(x)
print('*'*20)
y = [1,2]*2
y[0] = 100
print(y)
--------------------------------
[[1, 2], [1, 2]]
[[100, 2], [100, 2]]
********************
[100, 2, 1, 2]
注意这种嵌套的 list(包含子对象)
结论:浅拷贝问题
list 删除元素
- remove(value)
- 返回值为 None
- 从左到右查找到第一个匹配的 value 值并移除
- 修改原 list
- pop(index)
- 不指定 index,从尾部弹出一个元素
- 指定 index,从索引处弹出一个元素,索引越界抛出 IndexError 异常
- 返回值为 pop 出的对象,且原 list 被修改
- clear()
- 清除所有元素,原 list 为空列表
- 返回值为 None
list 其他操作
- reverse()
- 将列表元素反转
- 返回 None
- 修改原 list
- sort(key=None,reverse=False)
- 对原 list 进行排序,修改原列表,默认为升序
- 返回值为 None,修改原 list
- key 是一个函数
- in 和 not in
- 返回 bool 值
赋值、深拷贝、浅拷贝
# 赋值
a = [1,2,3,[4,5]]
b = a
a[0] = 100
a[3][0] = 100
print((a,b))
---------------------------------
([100, 2, 3, [100, 5]], [100, 2, 3, [100, 5]]) # 相互影响
a = [1, 2, 3]
b = a
b.append(4)
print(a) # 输出 [1, 2, 3, 4]
# 浅拷贝1
a = [1,2,3,[4,5]]
b = a.copy() # b = list(a) 也是浅拷贝 或import copy后使用b = copy.copy(a)
a[0] = 100
print((a,b))
a[3][0] = 100
print((a,b))
----------------------------------
([100, 2, 3, [4, 5]], [1, 2, 3, [4, 5]])
([100, 2, 3, [100, 5]], [1, 2, 3, [100, 5]]) # 仅子对象相互影响
# 深拷贝
import copy
a = [1,2,3,[4,5]]
b = copy.deepcopy(a)
a[0] = 100
print((a,b))
a[3][0] = 100
print((a,b))
----------------------------------
([100, 2, 3, [4, 5]], [1, 2, 3, [4, 5]]) # 不相互影响
([100, 2, 3, [100, 5]], [1, 2, 3, [4, 5]]) # 不相互影响
结论:
变量的赋值、浅拷贝和深拷贝都是用来创建对象的副本
- 赋值操作
赋值操作是将一个对象的引用赋值给一个变量,这个变量与原对象共享同一个内存地址。这意味着如果更改变量的值,也会更改原始对象的值 - 浅拷贝
浅拷贝是创建一个新对象,该对象与原始对象的内容相同,但是该对象的一些或全部元素是原始对象的引用。这意味着如果更改了浅拷贝对象中的引用对象,那么原始对象也会受到影响 - 深拷贝
深拷贝是创建一个完全独立的新对象,它与原始对象的内容相同,但该对象中的所有元素都是原始对象的副本,而不是原始对象的引用。这意味着如果更改深拷贝对象中的引用对象,原始对象不会受到影响
需要注意的是,深拷贝可能会比浅拷贝更耗费计算资源,因为它需要递归地遍历整个对象,创建对象的完全独立副本。因此,在处理大型对象或嵌套层数较深的对象时,深拷贝可能会更加耗时
元组tuple
- 有序的元素组合
- () 表示
- 不可变 对象
tuple 定义及初始化
- t = tuple()
- t = ()
- t = tuple(iterable) 如 tuple(range(3))
- t = (1,2,3)
- t = (1,)
- t = (1,)*5
tuple 元素访问
- tuple[index]
tuple 查询
- index(value[,start,[stop]])
- 匹配到第一个立即返回索引
- 匹配不到,ValueError 异常
- 时间复杂度 O(n)
- count(value)
- 返回匹配 value 的次数
- 时间复杂度 O(n)
- len(tuple)
tuple 其他
- 元组是只读的,增改删方法都没有
命名元组 namedtuple
- 查看官方文档实例
- 官方 chm 文件下载下载页面
- collections.namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
>>> x, y = p # unpack like a regular tuple
>>> x, y
(11, 22)
>>> p.x + p.y # fields also accessible by name
33
>>> p # readable __repr__ with a name=value style
Point(x=11, y=22)
切片
- 通过索引区间访问线性结构的一段数据
- [start:stop] 包含 start,不包含 stop,即 [start,stop)
- [:] 表示取全部
- [start:stop:step],step 为步长,与 start:stop 同向,否则返回空序列
列表和元组存储方式比较
l = [1, 2, 3]
tup = (1, 2, 3)
print(l.__sizeof__(), tup.__sizeof__())
-----------------------------
64 48
由于列表是动态的,所以它需要存储指针,来指向对应的元素(上述例子中,对于 int 型,8 字节)。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。
l = []
print(l.__sizeof__()) # 空列表的存储空间为40字节
l.append(1)
print(l.__sizeof__()) # 72 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2)
print(l.__sizeof__()) # 72 由于之前分配了空间,所以加入元素2,列表空间不变
l.append(3)
print(l.__sizeof__()) # 72 同上
l.append(4)
print(l.__sizeof__()) # 72 同上
l.append(5)
print(l.__sizeof__()) # 104 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间
但是对于元组,情况就不同了。元组长度大小固定,元素不可变,所以存储空间固定
列表和元组的选择
总的来说,列表和元组都是有序的,可以存储任意数据类型的集合,区别主要在于下面这两点:
- 列表是动态的,长度可变,可以随意的增加、删减或改变元素。列表的存储空间略大于元组,性能略逊于元组
- 元组是静态的,长度大小固定,不可以对元素进行增加、删减或者改变操作。元组相对于列表更加轻量级,性能稍优
应用场景:
- 如果存储的数据和数量不变,比如你有一个函数,需要返回的是一个地点的经纬度,然后直接传给前端渲染,那么肯定选用元组更合适
- 如果存储的数据或数量是可变的,比如社交平台上的一个日志功能,是统计一个用户在一周之内看了哪些用户的帖子,那么则用列表更合适
参考
- magedu