在经过对Python这门语言有个基本认识之后,我们知道Python中的所有变量和常量都是对象,这带来一个非常不错的便利,就是我们不需要关心基本的数据类型,所以这里就不再探究Python的基础变量,直接进入到Python中几种常见的数据结构。
Python中最常用的几种数据结构有:
列表 List
元组 Tuple
字典 Dic
集合 Set
而这其中,我们从List开始学习和了解。
列表的基本概念
基本概念
《Head first Python》对列表有一个定义:一个有序的对象集合。
这个定义还是非常准确的,有两个要点:
-
有序
-
对象集合
有序是说它的顺序是固定的,一次访问和一百次访问数据取到的顺序是一样的。
这在数学上叫做操作的幂等性?大学毕业太久记不太清了,意思大家能理解吧。
对象集合,顾名思义这个容器装的都是对象,而Python中所有东西都是对象,那这个容器自然也可以包含一切。
注意,这里的集合只是一个一般性概念,和后边的Set,这种数学意义上的集合不是一回事。
《Head first Python》中多次强调Python中的列表和其它语言中的数组类似,这点当然没错,从切片访问、定义等等方面来看都是如此,但同时它也有链表的特性,比如灵活的增删,可以动态扩展容量等。
可以说,Python的列表同时具有其它语言中数组和链表的特性,这很像JAVA中的抽象类List
,它同时具有其派生类ArrayList
和LinkedList
的特性。
了解了这些特点,我们就可以理解一些列表的独特行为。
声明
基本定义
列表的定义和声明相当简单且直观:
a=[1,2,3,4,5]
同时需要注意的是如上边所说,列表是一个对象集合,既然是对象集合,自然可以包含任意类型的数据,而且可以是异构的,就是说可以是不同数据类型的数据:
a=[1,2,3,4,5,"hellor",12.5]
当然,多层嵌套列表也不在话下:
a=[1,2,3,[4,[5,6]]]
类型转换
此外,列表还可以通过一个函数list()
将其它数据类型转换成列表:
string = "Hellow Wolrd"
a = list(string)
print(a)
# ['H', 'e', 'l', 'l', 'o', 'w', ' ', 'W', 'o', 'l', 'r', 'd']
是不是很神奇?
list()
是Python的一个魔术函数,可以在类定义中进行重载,这点在后边会详细说明,可以简单的理解为JAVA中的包装类,可以实现自动的类型转换。
使用range
除了转换其它类型的数据,Python还可以通过函数range()
来实现快速创建数字队列。
a=list(range(6))
print(a)
# [0, 1, 2, 3, 4, 5]
其实range(start,stop,step)
可以接受三个参数,分别是开始,结束和步进,步进甚至可以是负数,进行倒序构建队列:
a = list(range(10, 0, -1))
print(a)
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
列表的常见操作
对于一个数据结构,最基础的操作莫非增删改查,我们就从这些操作开始了解 列表。
查询
Python提供简单的for/in
操作对列表进行遍历:
a = list(range(5, 0, -1))
for num in a :
print(num)
# 5
# 4
# 3
# 2
# 1
添加
尾部添加
最常见的添加操作是在尾部追加新的元素:
a = list(range(5, 0, -1))
a.append(99)
print(a)
# [5, 4, 3, 2, 1, 99]
任意部位添加
如果想在其它位置插入一个元素,可以使用insert()
:
a = list(range(5, 0, -1))
a.insert(0,99)
print(a)
# [99, 5, 4, 3, 2, 1]
其中insert(0,99)
表示在第0个元素之前插入元素99,也就是在首部插入。
合并其它队列
此外,可以使用extend()
将其它列表合并到当前列表的尾部:
a = list(range(5, 0, -1))
a.extend([98,99])
print(a)
# [5, 4, 3, 2, 1, 98, 99]
删除
从尾部删除
可以使用pop()
从队列的尾部删除一个元素,这个操作有点像其它语言中的出栈操作:
a = list(range(5, 0, -1))
a.pop()
print(a)
# [5, 4, 3, 2]
从指定位置删除
pop是可以指定索引值的,当指定一个索引的时候就是从指定部位删除元素:
a = list(range(5, 0, -1))
a.pop(1)
print(a)
# [5, 3, 2, 1]
注意,要时刻清醒地认识到列表同时具有数组和链表两种特性,当删除一个元素后,列表会立即收缩,其长度和索引会发生改变,如果认识不到这一点,就会出现一些难以理解的事情:
a = list(range(5, 0, -1))
index = 0
for val in a:
if index == 1 or index == 2:
a.pop(index)
index += 1
print(a)
# [5, 3, 1]
乍一看,这个程序中初始的列表为[5,4,3,2,1]
,在遍历列表的时候我们使用索引直接删除第二个和第三个元素,结果应该是[5,2,1]
才对,结果却并不是。如果牢记上边的说明就能理解,当删除第二个元素时,列表的结构已经发生改变,变成了[5,3,2,1]
,而此时我们使用的累加索引index
已经不能准确地定位改变后地索引了,此时的第三个元素已经变成了2
,所以自然结果也就变成了[5,3,1]
。
对于这个问题,我觉得应该遵守如果你要用数组的方式遍历和删除元素,那也应该按照数组的方式进行这一原则。
我们来看代码:
a = list(range(5, 0, -1))
index = 0
for val in a:
if index == 1 or index == 2:
a[index] = None
index += 1
print(a)
# [5, None, None, 2, 1]
这样就没问题了,将第二个和第三个元素用None来标记为已删除,这才是使用数组的方式遍历和删除元素。当然,如果你不希望有None存在,可以额外地再循环匹配None并删除。
此外,如果你不想陷入此类的陷阱,完全可以通过将结果直接放入另一个队列而非直接在当前队列删除的方式,这样做就完全不存在相关问题:
a = list(range(5, 0, -1))
index = 0
b = []
for val in a:
if not(index == 1 or index == 2):
b.append(val)
index += 1
a = b
print(a)
# [5, 2, 1]
删除匹配的元素
队列还支持对匹配到的第一个相同元素进行删除:
a = list(range(5, 0, -1))
a.remove(1)
print(a)
# [5, 4, 3, 2]
变更
变更一个元素和数组的操作类似:
a = list(range(5, 0, -1))
a[1] = 99
print(a)
# [5, 99, 3, 2, 1]
其实对列表使用[]进行操作在Python中称作切片,有更多功能,在后文会说明。
列表的切片操作
使用[]
对列表操作在Python中成为切片,而且切片不止是能简单地对列表地单个元素进行访问,事实上它支持类似range()
那样的操作,比如[start:stop:step]
这样,可以直接截取列表中的一段:
a = list(range(10, 0, -1))
b = a[1:5:1]
print(a)
print(b)
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
# [9, 8, 7, 6]
这里截取了索引为1
到4
(不包括5
)的元素。
此外,如同range()
中一样,切片同样可以逆序截取:
a = list(range(10, 0, -1))
b = a[-1:5:-1]
print(a)
print(b)
# [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
# [1, 2, 3, 4]
[-1:5:-1]
表示从倒数第一个元素开始,到索引值为5的元素为止截取,因为元素5的索引值正好也是5,所以截取的元素中恰好不包括5。对,在切片操作中索引值是可以为负数的,负数表示倒数,比如-2
就表示倒数第二个元素。
当然,迭代步数step
也可以是其它数值,这里不做一一演示。
更多关于列表的Python式操作可以阅读
关于列表的进阶内容可以阅读。
本系列文章的代码都存放在Github项目:。
文章评论