定义
列表是 Python 中最基本的数据结构。在 Python 中,用一个方括号表示一个列表——[]
。列表中的每个元素都分配有一个数字—它的位置,或索引,第一个索引是0,第二个是1,以此类推。列表与数组是比较相似的。
创建列表
创建一个空列表
>>> a = []
>>> type(a)
<class 'list'>
>>> bool(a)
False
>>> print(a)
[]
上面创建了一个空列表,我们用type()
函数查看其类型为list
,然后用bool()
函数查看并把其打印出来,也证明了我们创建的是一个空列表。下面创建一个非空列表:
>>> a = [1,2.0,'Python',None]
>>> a
[1,2.0,'Python',None]
>>> type(a)
<class 'list'>
>>> bool(a)
True
>>> print(a)
[1,2.0,'Python',None]
上述的例子中,我们创建了一个非空列表类型对象。上面我们说到列表与数组很相似,但从这里我们可以观察到明显的区别。可以看到在上述的例子中,列表包含了四种类型:整型、浮点型、字符串、None;数组内的元素要求必须同一类型,但列表不同,列表中的元素可以是任意类型,当然也包括嵌套列表;不仅如此,列表中的元素个数可以是无限个,但数组不能。
>>> a = [1,'Python']
>>> b = [2.0,None]
>>> c = [a,b]
>>> c
[[1, 'Python'], [2.0, None]]
访问列表
-
索引
与数组类似,列表也可以用索引的方式来访问元素,比较特殊的一点是 Python 支持负索引。
列表a 2 5 1 8 3 4 正索引 0 1 2 3 4 5 负索引 -6 -5 -4 -3 -2 -1 >>> a = [2,5,1,8,3,4] >>> a[0] 2 >>> a[3] 8 >>> a[5] 4 >>> a[-1] 4 >>> a[-3] 8 >>> a[-6] 2
对于负索引,如
a[-3]
实际上就是a[6+(-3)] = a[3]
,理解这一点,在接下来要介绍的切片中我们也会用到负索引。 -
切片
除了通过索引访问单个元素的方式外,Python 中还有一种切片的访问方式可以一次性访问多个元素。(实际上我们在讲字符串时已经介绍过)基本操作就是在冒号的两边加上要访问的索引值。
>>> lst = [2,5,3,1,4] >>> lst[1:4] [5, 3, 1]
如上例子,可以实现一次性访问索引1到4但不包过4的子序列。接下来看下负索引的方式:
>>> lst[-3:-1] [3, 1]
需要注意的是,不管是用正索引还是负索引,在冒号的两边,左边的值必须是小于右边的值否则会返回空列表。
>>> lst[4:1] [] >>> lst[-1:-3] []
还有一些特殊的边界值:
# lst[0:4] 等效于 lst[:4] >>> lst[0:4] [2, 5, 3, 1] >>> lst[:4] [2, 5, 3, 1] # lst[1:5] 等效于 lst[1:] >>> lst[1:5] [5, 3, 1, 4] >>> lst[1:] [5, 3, 1, 4] # lst[:]可以访问整个列表元素 >>> lst[:] [2, 5, 3, 1, 4]
上面只用了正索引作为示例,负索引也是同样的,这里不再举例。但是,这里发现一个不一样的地方,
lst
长度为5,在上例中lst[1:5]
是可以正常访问的,但按道理是没有5这个索引的,不过由于我们前面说过,冒号右边的索引是不包括在内的,也就是只访问该索引的前一个位置。看似正常,那我们继续来看下面的例子:>>> lst[1:8] [5, 3, 1, 4] >>> lst[1:10] [5, 3, 1, 4]
可以看到冒号右边的数字已然超过列表的长度值,但访问的结果与
lst[1:5]
是一致的。实际上如果冒号右边的数字大于或等于列表的长度,但由于列表没有那么多元素,所以访问到的最多依然是列表的最后一个值;虽说如此,但不建议这样使用。上面是使用单个冒号的情况,在切片访问方式中还可以使用两个冒号获取带间隔的序列元素,两个冒号最后的数值就是间隔长度:
>>> lst = [1,2,3,4,5,6,7,8,9,10] >>> lst[0:11:2] [1, 3, 5, 7, 9]
start:stop:interval
实际上这就是最全的切片结构,如上所示,获得的切片为:索引从0到11间隔(步长)为2的序列元素组成的子序列。
反转列表
所谓反转列表,即将[1,2,3]
变为[3,2,1]
,因为在编程中经常会用到,所以这里独立出来单独讲。
-
使用切片
实际上我们用到的还是上面提到的列表的切片访问方式。
>>> lst = [1,2,3,4,5,6] >>> lst[::-1] [6, 5, 4, 3, 2, 1] >>> lst [1, 2, 3, 4, 5, 6]
可以看到,反转后对原来的对象并没有影响。
lst[::-1]
的本质是从右边(符号,表示反方向)看这个列表,然后按照步长为1取列表中的元素,并生成新的列表。如果将-1
换成1
,即lst[::1]
,这与lst[::]
结果一致。虽然切片操作简单,但在实际应用中不推荐使用,下面介绍一种较常用的方法。 -
使用
reversed()
函数使用
reversed()
函数可以将列表对象进行反转,不过该函数返回的是一个迭代器,因此我们还需要将其再转化为列表再表示出来。>>> a = [3,2,5,4] >>> b = list(reversed(a)) >>> b [4, 5, 2, 3]
注: 迭代器后续会详述。
-
使用
reverse()
函数>>> a = [3,2,5,4] >>> a.reverse() >>> a [4, 5, 2, 3]
reverse()
与reversed()
虽然名字很相似,都可以反转列表元素,但不同的是,reverse()
是原地反转,即对原列表作用,是没有返回值的。 -
使用
sorted()
或sort()
函数>>> a = [1,2,3,4,5] >>> b = sorted(a,reverse=True) >>> b [5, 4, 3, 2, 1] >>> a = [5, 4, 3, 2, 1]
>>> a.sort() >>> a [1, 2, 3, 4, 5]
利用
sorted()
函数及reverse
参数对原列表进行排序后反转,resverse = True
表示按降序排序,resverse = False
表示按升序排序。如上所述,需要对列表对进行排序,所以这种方法只适用于有序列表的反转。
操作列表(访问、添加、删除)
-
添加元素
列表与数组、字符串不一样的是其元素时可以改变的,且支持动态添加元素。
使用
append()
或insert()
添加单个元素:>>> a = [1,2,3,4] >>> a.append(5) # append()方法默认在列表尾部添加元素 >>> a [1, 2, 3, 4, 5] >>> a.insert(2,6) # insert()方法在指定索引处添加元素,这里在索引为2处添加元素6,原始该位置及后面的元素都往后挪动 >>> a [1, 2, 6, 3, 4, 5]
使用
extend()
或+
实现一次添加多个元素:>>> a.extend([7,8]) >>> a [1, 2, 6, 3, 4, 5, 7, 8] >>> b = a + [9,10] >>> a [1, 2, 6, 3, 4, 5, 7, 8] >>> b [1, 2, 6, 3, 4, 5, 7, 8, 9, 10]
值得注意的是,在使用
extend()
方法为列表添加多个元素时,并没有创建一个新的列表,而是直接在原列表进行添加,这被称为in-place
即就地。而使用+
则是创建了一个新的列表对象,所以在上述例子中我们用一个新的变量接收了该对象,也因此此种方式不是就地批量添加元素。>>> a = [1,2,3,] >>> b = "abc" >>> a.extend(b) >>> a [1, 2, 3, 'a', 'b', 'c']
从上面的例子中可以看到,在
extend
字符串时,字符串是被以字符为单位拆开后追加到了a
里面。extend()
的参数必须是iterable
类型对象,即可迭代对象。这里再提一点就是我们常用的+=
操作符实际就是调用extend()
方法来进行合并运算。 -
删除元素
remove()
直接删除元素,若被删除元素不在列表中,则会报错,若在列表内重复出现多次,则只删除第一次出现的那个。>>> a = [1, 2, 3,2, 'a',2, 'b', 'c'] >>> a.remove('d') Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: list.remove(x): x not in list >>> a.remove(1) >>> a [2, 3, 2, 'a', 2, 'b', 'c'] >>> a.remove(2) >>> a [3, 2, 'a', 2, 'b', 'c']
pop()
删除指定索引位置的元素,若不带参数则默认删除列表最后一个元素,该方法返回的结果是被成功删除的元素。>>> lang = ['Python','Java','PHP','C++','SQL'] >>> lang.pop() 'SQL' >>> lang ['Python', 'Java', 'PHP', 'C++'] >>> lang.pop(2) 'PHP' >>> lang ['Python', 'Java', 'C++']
同时,如果索引超出范围,也是同样会报错的
>>> lang ['Python', 'Java', 'C++'] >>> lang.pop(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: pop index out of range
del
与pop
相似,同样是删除索引处的元素,不一样的是del
一次性可以删除多个元素,通过切片方式:>>> del lang[-1] >>> lang ['Python', 'Java'] >>> del lang[:] >>> lang []
-
列表与
in
列表是可迭代的,除了使用常规的索引遍历外,还支持
for item in alist
这种直接遍历元素的方法。>>> lst = [2,5,6,4,3,1] >>> for item in lst: ... print(item) ... 2 5 6 4 3 1
in
与可迭代器结合,还可以用于判断某个元素是否属于此列表:>>> lst = [2,5,6,4,3,1] >>> 4 in lst True >>> 7 in lst False
-
列表与数字
内置的list通过
*
与数字结合,可以实现重复序列的操作:>>> a = ["hi","Hello"]*3 >>> a ['hi', 'Hello', 'hi', 'Hello', 'hi', 'Hello']
使用列表与数字相乘构建二维列表:
>>> a = [[]] * 3 >>> a[0] = [1,2] >>> a[1] = [3,4] >>> a[2] = [5,6] >>> a [[1, 2], [3, 4], [5, 6]]
-
求列表长度
使用
len
求取列表长度:>>> a = [1,2,3,4,5,6] >>> len(a) 6
-
求列表中元素的最值
>>> a = [1,2,3,4,5,6] >>> max(a) 6 >>> min(a) 1
-
修改列表元素
前面讲到列表与数组、字符串不同的地方就是列表是可变的,这也意味着它的元素时可以改变的:
>>> a = ['Python','java','C','C++'] >>> a[1] = 'PHP' >>> a ['Python', 'PHP', 'C', 'C++']
列表生成式
列表生成式是创建列表的一种简洁又方便且强大的方法。
使用列表生成式创建1到100的所有偶数列表:
>>> lst = [i for i in range(1,100) if i%2==0 ]
列表常用函数
实际上在上面已经提到许多方法了,除此之外列表还有如下常用封装的方法。
-
clear()
clear
用于清空列表内的所有元素:>>> a = [1,2,3,4,5,6] >>> a.clear() >>> a []
-
index()
index()
用于查找指定元素在列表内的索引:>>> a = [1,2,3,4,5,6] >>> a.index(5) 4
-
count()
count
用于统计某元素在列表中出现的次数:>>> a = [1,2,3,5,2,6,7,2] >>> a.count(2) 3
-
sort()
sort
用于对列表内元素进行排序,方法有两个参数,sort(key=None,reverse=False)
,其中参数key
定制排序规则,reverse
参数决定排序方式。# 默认情况下,key=None,reverse=False,即没有排序规则,按升序排序 >>> a = [2,5,3,1,4,6] >>> a.sort() >>> a [1, 2, 3, 4, 5, 6]
reverse
参数比较好理解,下面介绍下参数key
怎么使用。按照字符串元素长度大小进行排序:
>>> lst = ['Python','C','Java','C++','JavaScript'] >>> lst.sort(key=len) >>> lst ['C', 'C++', 'Java', 'Python', 'JavaScript']
如下示例,列表元素为元组,根据元组第二个值由小到大排序:
>>> a=[(3,1),(4,2),(1,3),(5,4),(9,-10)] >>> a.sort(key=lambda x:x[1]) >>> a [(9, -10), (3, 1), (4, 2), (1, 3), (5, 4)]
在前面我们介绍过
sorted()
函数,两者的区别在于sort
是应用在list
上的方法,而sorted
可以对所有可迭代的对象进行排序操作。且list
的sort
方法是对列表进行就地操作,没有返回值,而sorted
返回的是一个新的list
。
列表包含自身
列表的赋值操作,有一个有意思的问题:
>>> a = [1,2,3,4]
>>> a[2] = a
>>> a
[1, 2, [...], 4]
上面的输出结果,中间省略号表示无限循环,也就是说a[2] = a
这种赋值操作导致了无限循环,这是为什么呢?
在执行a = [1,2,3,4]
的时候, Python 会先创建一个列表对象[1,2,3,4]
,然后变量a
指向该对象。而在执行a[1] = a
时,Python 做的是把列表对象的第二个元素指向a
所引用的列表对象,即列表对象本身。这样,如果画图的话实际上这已经形成一个环路:a[1]->a->a[1]
,所以会导致无限循环。在实际编程中切记避免此种赋值操作。
列表和字符串转化
在符合某些条件的情况下,可以实现列表和字符串之间的转化。这里会使用到split()
和join()
,这两个方法实际上我们在介绍字符串时已经有介绍过。
-
split()
>>> str = "Python is a clean language" >>> str.split(" ") ['Python', 'is', 'a', 'clean', 'language']
-
join()
join
可以说是split
的逆运算,将列表元素连接成一个字符串>>> a = ['Python', 'is', 'a', 'clean', 'language'] >>> "".join(a) 'Pythonisacleanlanguage' >>> " ".join(a) # 以空格作为连接分隔符 'Python is a clean language'
深浅拷贝
list封装有cpoy
方法实现对列表的浅拷贝,何为浅拷贝?
>>> a = [1,3,6]
>>> a1 = a.copy()
>>> a1
[1, 3, 6]
>>> a1[1] = 4
>>> a1
[1, 4, 6]
>>> a
[1, 3, 6]
a1
与a
分别指向不同内存地址的对象,从上述a1
改变列表内元素就可以知道这点,但如果列表有嵌套情况:
>>> a = [[1,2],3,4]
>>> a1 = a.copy()
>>> a1
[[1, 2], 3, 4]
>>> a1[1] = 5
>>> a1
[[1, 2], 5, 4]
>>> a
[[1, 2], 3, 4]
>>> a1[0][1] = 3
>>> a1
[[1, 3], 5, 4]
>>> a
[[1, 3], 3, 4]
从上面的示例结果中可以看出,对于copy
来说内嵌的列表并没有实现拷贝,而是指向原列表对象中的嵌套列表,所以在a1
更改嵌套列表中元素时,a
中的嵌套列表的元素也随之发生改变。如果不想更改a
中的元素,我们可以用copy
模块中的深度拷贝deepcopy
来实现。
>>> a = [[1,2],3,4]
>>> import copy
>>> a1 = copy.deepcopy(a)
>>> a1
[[1, 2], 3, 4]
>>> a1[0][1] = 5
>>> a1
[[1, 5], 3, 4]
>>> a
[[1, 2], 3, 4]