文章目录
1. 元组和列表的区别
列表和元祖都属于可以存储任意数据类型的有序集合,他们的区别用一句话总结就是:
元组和列表的关系就是,元组中元素不可变,列表中是可以变化的
元组
一旦创建出来,元组就不能修改元组中所包含的元素,长度大小固定,无法增加删减或者改变,静态序列
,列表
创建之后,可以增加、修改或者删除元素,属于动态序列
>>> lt = [10, 20, 30, 40]
> lt[3] = 40 # 索引从0开始,lt[3] 表示访问列表的第四个元素
> lt
[1, 2, 3, 40]
> tup = (1, 2, 3, 4)
> tup[3] = 40 # 修改元素的时候报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
2. 区别决定场景
2.1 元组(静态)
2.1.1 运维场景
linux运维过程中,经常会结合一些脚本返回给监控软件,这些使用一般就是key-value这种形式,这些key一般是固定的,因为调用的bash命令输出是固定不变的。比如内存的情况,我们比较关心内存的容量,剩余容量,可用容量,一般我们会用free命令,那么对应的key就是total, free, aviable。这三个值在处理过程中,我们不希望被修改的。
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#######################
#
# Author: hao.zhang
# Time: 2020-05-24
#
#######################
import subprocess
def Run(cmd):
'''
cmd => linux操作系统系统命令,格式为列表
return => 返回为列表,包括返回码、命令输出、报错信息
'''
c = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
code = c.wait()
output = c.communicate()
out = output[0]
err = output[1]
return code, out, err
def get_mem_info():
cmd = ['free', '-h']
code, out, err = Run(cmd)
mem_info=out.split()
mem_key=(mem_info[7], mem_info[9], mem_info[12])
return mem_key
if __name__ == '__main__':
mem_result=get_mem_info()
print mem_result
输出
2.1.2 数据安全
有时候我们格式化一批数据的时候,我们程序的处理格式是有要求的,那么对于数据,我们希望是不可改变的,这样保证数据处理。比如登记基本信息,我们希望是(姓名,年龄,性别),这个数据在处理过程中不能被误改的。
样例
#!/usr/bin/env python
#coding:utf-8
info = ("haozi", "man", 18)
print("姓名是:%s 性别是 %s 年龄是 %d" %info)
输出
2.2 列表(动态)
对于列表,我能想出来的的场景就是对于数据进行批量处理,比如有一批数据,我们需要从GB转化为MB这种操作。
3. 元组和列表的共通点
3.1 通过索引处理元素
3.1.1 正数索引
列表个元组都支持索引操作,索引都是从0开始,第一个元素对应的索引为0
3.1.2 负数索引
倒数第一个元素对应的索引为-1
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
test_tup = ('test1', 'test2', 'test3', 'test4')
print("索引为0的元素: %s" % test_tup[0])
print("索引为-1的元素: %s" % test_tup[-1])
输出
3.2 支持切片处理
用法:[start: end: step]
start和end 两个索引值都可使用正数或负数 , 其中负数表示从倒数开始 。 该语法表示从start索引的元素开始(包含),到end索引的元素结束(不包含) 的所有元素。
step 表示步长,因此 step 使用负数没有意义
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
test_tup = ('test1', 'test2', 'test3', 'test4')
# 输出索引0<< index < 3
print test_tup[0:3]
# 输出索引1<< index < 3
print test_tup[1:3]
# 输出索引0<< index < 3,间隔为2
print test_tup[0:3:2]
输出
3.3 嵌套
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
test_tup = (1, 2, (3, 4, 5), 6, 7, (8, 9))
test_tup1 = (1, 2, [3, 4, 5], 6, 7, (8, 9))
test_list = [10, 11, 12, [13, 14, 15], 16, [17, 18, 19]]
test_list1 = [10, 11, 12, (13, 14, 15), 16, [17, 18, 19]]
print test_tup[2]
print test_list[3]
print tuple(test_list[5])
print test_list
print "==========================="
print test_tup1[2]
print test_list1[3]
print tuple(test_list1[5])
print test_list1
输出
3.4 封包和解包
- 封包:程序将多个值赋值给一个变量,python将多个值自动封装成一个元组,这个称谓序列封包
- 解包:元组或列表可以赋值给多个变量,此时序列的各元素会被依次赋值给每个变量,
要求序列的元素个数和变量个数相等
。这种功能被称为序列解包。
3.4.1 异步方式
- 一个变量对应多个值的时候,会将这些值组成一个元组。
- 将一个元组对应多个值,将元组中元素按照顺序复制给变量,所以元组个数和变量个数要保持一致。
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 封包
test = 1, 2, 3, 4, 5
print("test变量:%s , test变量类型:%s" % (test, type(test)))
# 解包
test2 = tuple(range(0, 10, 2))
print test2
a, b, c, d, e = test2
print a, b, c, d, e
输出
3.4.2 同步方式
- 封包和解包同时应用,一个表达式可以搞定,其实内部执行顺序和上面异步的方式是相同的
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
x, y, z = 1, 2, 3
print("x:%d, y:%d, z:%d" % (x, y, z))
输出
3.5 常用内建函数
函数 | 列表 | 元组 | 函数作用 |
---|---|---|---|
count | 支持 | 支持 | list.count(n),列表或者数组中元素是n的个数 |
index | 支持 | 支持 | list.index(n), 列表或者元组中元素为n的索引 |
append | 支持 | 不支持 | 添加元素 |
reverse | 支持 | 不支持 | 列表支持,对列表进行倒置 |
sort | 支持 | 不支持 | 列表支持,对列表元素进行排序,数据按照大小,字符串按照 |
reversed | 支持 | 支持 | 元组支持,返回一个迭代器,然后通过list输出为列表 |
sorted | 支持 | 支持 | 元组支持,返回一个迭代器,然后通过list输出为列表 |
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
ltest = [3, 2, 3, 4, 7, 1]
ttest = (3, 2, 3, 4, 7, 1)
print("count函数,元素为3的个数:%d" % ltest.count(3))
print("index函数,元素为7的索引:%d" % ltest.index(7))
print "================tuple============================"
print("count函数,元素为3的个数:%d" % ttest.count(3))
print("index函数,元素为7的个数:%d" % ttest.index(7))
print("reversed函数,元素倒置:%s" % list(reversed(ttest)))
print("sorted函数,元素排序:%s" % sorted(ttest))
输出
4. 列表和元组的存储空间分配
4.1 定义序列,同时赋值
列表和元组的区别是一个可变,一个不可变,那么存储方式和空间分配也是不同的,通过__sizeof__可以查询目前列表或者元组消耗的空间字节数。
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
l = [1, 2, 3]
print("列表占用空间数:%d " % l.__sizeof__())
tup = (1, 2, 3)
print("元组占用空间数:%d" % tup.__sizeof__())
输出
可以看出来,同样的元素,在列表和元组两种方式下是不同的,列表占用的字节数明显高于元组的。
由于列表是动态的,所以需要指针指向存储位置,指向对应的元素。另外,由于列表可变,所以需要额外存储已经分配的长度大小,这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。
4.2 先定义序列,然后append
这个方式是针对列表方式,因为元组是不可以append的。
样例
#!/usr/bin/env python
# -*- coding:utf-8 -*-
l = []
print("空列表占用空间数:%d " % l.__sizeof__())
tup = ()
print("空元组占用空间数:%d" % tup.__sizeof__())
for i in range(10):
l.append(i)
print("%d个元素空列表占用空间数:%d " % (i, l.__sizeof__()))
输出
从上面可以看出,空列表占用40个字节,元组占用24个字节,列表再append一次,会申请32个字节的空间,知道空间用完之后,再次进行申请
所以元组的空间开销要比列表小,对于大型数据量的话, 这个空间开销会更加明显
5. 元组和列表性能开销
从下面的程序可以看出来,元组创建效率要比列表搞,但是索引的时间是差不多的
5.1 创建性能对比
python3 -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop
5.2 索引性能对比
python3 -m timeit -s 'x=[1,2,3,4,5,6]' 'y=x[3]'
10000000 loops, best of 5: 22.2 nsec per loop
python3 -m timeit -s 'x=(1,2,3,4,5,6)' 'y=x[3]'
10000000 loops, best of 5: 21.9 nsec per loop
- 参考资料:
- 极客时间 景霄 python核心技术与实战
- 疯狂python讲义