1-Python - list

about

列表是Python中最常用的数据类型之一,也是最灵活的数据类型之一,其可以包含任何种类的对象:数字、字符串、元组、字典,也可以嵌套的包含列表。当然与字符串不同的是,列表是可变的,可变指的是我们在原处修改其中的内容,如删除或增加一个元素,则列表中的其他元素自动缩短或者增长,也正是如此,在列表元素个数过多时,如果你删除靠前的(如第一个)元素,其他的元素都要向前移动,会导致性能有所下降,这是在开发中需要注意的。

列表的基本操作

创建列表

在Python中,用一对中括号[]来表示列表(list),用,分割列表内的每个元素。

l = []
print(l)  # []
print(type(l))  # <class 'list'>
print(list('abc123'))  # ['a', 'b', 'c', '1', '2', '3']
print(list(range(-1, 4)))  # [-1, 0, 1, 2, 3] 

可以看出,列表中的元素可重复。
而我们不觉间用了一个内置函数range(),range(start,stop,step)为Python的内置函数,range函数在Python 3.x中返回一个区间范围,如果想要得到列表,就需要如上例第6行所示,显式使用list转化。start参数表示这个区间的起始位置,stop是区间结束的位置,step是步长,也就是在这个区间内,每几个取一个值。
需要注意的是,正如上例最后的返回结果所示,通过range取-1到4这个区间内的数值。-1可以取到,而4取不到,只取到3,这是range的特性,开始start的值能取到,而stop也就是最后一个值取不到,我们称这个特性为顾头不顾尾。
range函数一般在for循环内用的较多,举个例子。:

for i in range(-1, 10, 3):
    print(i)
'''
-1
2
5
8
'''

上例for循环的范围是-1到10,但由于10取不到只能实际取值到9,也就是-1 0 1 2 3 4 5 6 7 8 9这几个数字,在这个区间内取值, 并且由于step步长是3,所以每3个取一个,结果就是-1 2 5 8

列表合并(拼接): +

l1 = [1, 2, 3]
l2 = ['a', 'b', 'c']
l3 = l1 + l2
print(l3)  # [1, 2, 3, 'a', 'b', 'c']

合并(拼接)就是将两个列表合为一个新列表,原有列表不变。

列表的重复:*

l1 = [1, 2, 3]
l2 = l1 * 3
print(l2)  # [1, 2, 3, 1, 2, 3, 1, 2, 3]

重复可以理解为将原列表复制指定次数,然后相加得到一个新的列表。

列表成员资格判断:in,not in

l = ['a', 'b', 'we']
print('a' in l)  # True
print('w' not in l)  # True
print('we' in l)  # True

成员资格测试就是判断指定元素是否存在于列表中,存在则返回True,不存在则返回False。

通过索引取值

l = ['a', 'b', 1, 2]
print(l[0])  # a
print(l[6])  # IndexError: list index out of range

列表中每一个元素都有自己的索引(从0开始)位置,这也是为什么说列表是有序的原因。我们可以通过索引取对应的值。
注意,当通过索引取值时,索引范围超过列表索引长度时,会报错,因为没有这个索引位置。

切片

l = ['a', 'b', 'c', 'd', 'e', 'f', 'g']

# 从指定索引位置开始取值
print(l[3:])  # ['d', 'e', 'f', 'g']
# 取列表内的一段元素
print(l[3:6])  # ['d', 'e', 'f']
# 每隔两个取一个
print(l[1:6:2])  # ['b', 'd', 'f']
# 取索引为-2的元素
print(l[-2])  # f
# 从左向右取,从0开始,取到倒数第二个之前的
print(l[:-2])  # ['a', 'b', 'c', 'd', 'e']
# 反转列表
print(l[::-1])  # ['g', 'f', 'e', 'd', 'c', 'b', 'a']

切片是根据列表的索引来取值(取范围内的值),需要说明的是,只要是序列类型(字符串,列表,元组),其内的元素都有自己的索引位置,我们可以根据索引位置取值,切片。

for循环取值

l = ['a', 'b', 'c', 'd']
for i in l:
    print(i)
'''
a
b
c
d
'''

for循环取值时,每次循环取出一个元素,然后将这个元素赋值给i,我们就可以通过操作i来操作取出的元素。

列表元素更新

l = ['a', 'b', 'c', 'd']
print(l, id(l))  # ['a', 'b', 'c', 'd'] 128337600
l[1] = 'w'
print(l, id(l))  # ['a', 'w', 'c', 'd'] 128337600

通过指定的索引修改列表中对应的元素,并且通过打印结果发现,当列表内的元素被修改后,列表的内存地址不变。

删除列表(元素)

l = ['a', 'b', 'c', 'd']
del l[0]
print(l)  # ['b', 'c', 'd']
del l[0], l[1]
print(l)  # ['c']
del l
print(l)  # NameError: name 'l' is not defined

使用del关键字进行列表元素的删除,del支持删除多个元素。但要注意的是,删除多个元素的时候,需要牢记,要删除的第一个元素后面的元素此时还在索引范围内。
另外,当删除列表中的指定索引后,如果该索引后面还有元素,则后面所有的元素都会往前位移,或者称为补位。
当使用del删除整个列表后,这个列表就不存在了,也就是最后的报错。
还有一点需要注意:

l = ['a', 'b', 'c', 'd']
del l[1], l[2]
print(l, len(l))  # ['a', 'c'] 2
del l[3]  # IndexError: list assignment index out of range

原列表的最大索引为3,删除两个元素后,此时列表最大的索引为2,此时却要删除索引为3的元素,就抛出错误了。

列表的常用方法

我们来学习列表中都有哪些方法。

list.append(obj)
将一个对象(元素)追加到列表的末尾。这只是一个追加操作,并且是原地操作列表,所以该方法没有返回值。

l = ['a', 'b', 'c', 'd']
l.append(1)
print(l)  # ['a', 'b', 'c', 'd', 1]
l.append('w')
print(l)  # ['a', 'b', 'c', 'd', 1, 'w']

由打印结果可以看到,两个元素都已追加的形式被存放在列表的尾部。

list.insert(index, obj)
将一个对象(元素)插入到列表的指定索引的前面。这只是一个插入操作,并且是原地操作列表,所以该方法没有返回值。

l = ['a', 'b', 'c', 'd']
l.insert(1, 1)
print(l)  # ['a', 1, 'b', 'c', 'd']

可以看到,我们通过insert操作在列表索引为1的元素前面插入一个新元素1
需要注意的是,就如插队一样,前面插入一个人,后面的人都要往后移动一位,如果列表长度很长的话,会对性能有所影响。

In [2]: %timeit list(range(100000)).append('a')
3.25 ms ± 22.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [3]: %timeit list(range(100000)).insert(0, 'a')
3.36 ms ± 99.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

上例,我们通过ipython测试同样是十万个元素的列表,一个执行将元素追加到列表的尾部,一个执行将元素插入到列表的头部,可以看到,追加操作需要耗费3.25ms,而插入操作需要3.36ms

list.pop([obj])
移除列表中的指定索引位置的元素,若不指定索引,默认移除列表尾部的元素,并且将列表返回(返不返回是我的事儿,接不接收是你的事儿)。

l = ['a', 'b', 'c', 'd']
print(l.pop(1))  # b
print(l.pop())  # d
print(l.pop(9))  # IndexError: pop index out of range
print(l.pop('d'))  # TypeError: 'str' object cannot be interpreted as an integer

前三个打印好解释,最后一个打告诉我们字符串无法解释为整型,也就是说,list.pop(obj)移除操作要么是指定元素下标,要么什么都不指定,按照人家默认的来,而不能指定删除某个元素。

list.remove(obj)
删除列表中的指定元素,不指定或指定元素不存在报错,而且仅是删除操作,没有返回值。

l = ['a', 'b', 'c', 'd']

l.remove('a')
l.remove('c')
print(l)  # ['b', 'd']
l.remove(1)  # ValueError: list.remove(x): x not in list

最后报错说删除的指定元素不存在。

list.reverse()
反转列表,并没有参数和返回值,只是在原地对列表中的元素进行反转。

l = ['a', 'b', 'c', 'd']
l.reverse()
print(l)  # ['d', 'c', 'b', 'a']

list.sort(key=None, reverse=False)
原地对列表进行排序,key是自定义的排序算法,此函数在每次元素比较时被调用,reverse表示排序方式,默认的false是按照升序进行排序的,当reverse=True时,排序结果为降序。

l = ['a', 'c', 'b', 'd']
l.sort()
print(l)  # ['a', 'b', 'c', 'd']
l.sort(reverse=True)
print(l)  # ['d', 'c', 'b', 'a']

我们来看看key参数怎么使用:

l = ['abc', 'cae', 'edg', 'ffh']
l.sort(key=lambda x: x[1])
print(l)  # ['cae', 'abc', 'edg', 'ffh']

通过key指定以每个元素中的索引为1的元素排序。
需要补充的是sorted(key=None, reverse=False)函数,它是Python内置的对于所有序列类型进行排序的函数,而不像sort()方法是列表独有的排序方法。
二者区别是:

  • sort方法仅能对列表进行原地排序,没有返回值。
  • sorted函数对所有序列类型进行排序,并且并不是原地排序,它会返回排序结果,也就是说我们可以通过一个变量接收排序结果。

需要注意的是,无论使用哪种排序方式,排序对象必须是同类型的,也就是说,如果排序对象是列表,那么列表内的元素都为同一类型,因为不同类型无法比较。

来复习与总结,列表中的常用操作符:

操作符(表达式) 描述 重要程度
+ 合并 **
* 重复 **
in 成员资格 ****
for i in [1, 2, 3]:print(i) 迭代 *****
list[2] 索引取值 *****
list[start:stop:step]、list[::-1] 切片(截取) *****

列表中的常用方法:

方法 描述 重要程度
list.append(obj) 列表添加元素到末尾 *****
list.insert(index,obj) 列表添加元素到指定位置 *****
list.pop(obj) 删除列表元素 *****
list.remove() 删除列表元素 *****
list.reverse() 反转列表的元素 ****
list.sort() 排序 ***
list(seq) 将序列转换为列表 *****
list.extend(seq) 列表末尾追加多个值 ***
list.count(obj) 统计某个字符在列表内出现的次数 ****
list.index(obj) 找出指定元素的第一个匹配项的索引位置 ***

同样的,还有其他置函数或者声明语句可以应用于列表:

函数 描述 重要程度
len(list) 返回列表的元素个数 *****
max(list) 返回列表内最大的元素 **
min(list) 返回列表内最小的元素 **
cmp(list1, list2) 比较两个列表的元素 **
del obj1, obj2 删除列表的元素 ****

需要注意的,max()min()函数都需要列表内的元素类型保持一致,不然没法比较啊!
另外,cmp()函数在Python 3.x中不存在了,如果要使用类似功能,可以使用operator代替,它适合任何对象:

import operator
print(operator.eq([1, 2], [2, 1]))  # False

或者使用更简单的方式:

print([1, 2] > [2, 1])  # False
print([1, 2] == [2, 1])  # False
print([1, 2] != [2, 1])  # True

列表的嵌套

前文介绍列表时说的元素类型丰富,就是说列表不仅能存储数字、字符串,还能存储列表。

>>> l = [1, 2, [3, 4]]    
>>> for i in l:    
...     print(i)    
...    
1    
2    
[3, 4]    
>>> l[2]    
[3, 4]    
>>> l[2].pop()    
4    
>>> l    
[1, 2, [3]]    
>>> l[2].insert(0,'a')    
>>> l    
[1, 2, ['a', 3]]    

可以看到,列表对嵌套部分处理也同样的简单,可以使用我们学过的方法, 不仅如此,列表还可以存储别的数据结构,如字典、元组、集合。

>>> l = [1,(2, 3), [4,[5, 6]], {'a':'b'}, {7, 8}]    
>>> for i in l:    
...     print(i)    
...    
1    
(2, 3)    
[4, [5, 6]]    
{'a': 'b'}    
{8, 7}    
>>> l[1][1]    
3    
>>> l[2][1][1] = 'c'    
>>> l[3]['a']    
'b'    
>>> l[-1]    
{8, 7}    
>>> l    
[1, (2, 3), [4, [5, 'c']], {'a': 'b'}, {8, 7}]    

先不管元组、字典、集合的是什么。但并不推荐这么用,因为这样操作起来太不方便,只是演示列表可以各种嵌套,一般使用中,更多的是嵌套一种数据类型,如列表嵌套元组,列表嵌套字典,但很少有同时嵌套元组和字典的。
那么,我们如何展示列表中的所有元素呢?这里我们可以使用嵌套循环完成。

>>> for i in [1, [2, 3], 4]:  
...     if isinstance(i, list):  
...         for j in i:  
...             print(j)  
...     else:  
...         print(i)  
...  
1  
2  
3  
4  

上例中,第1行for循环列表,第2行判断每次循环中的元素是否为列表,如果是列表,那么就再用for循环循环打印其内的列表中的元素。否则执行第5行的else语句直接打印列表内的元素。
需要强调的是,Python中并没有二维数组的概念,但是列表嵌套列表同样能够达到相同的目的不是吗?


欢迎斧正,that's all,see also:

什么是序列,Python序列详解(包括序列类型和常用操作)

posted @ 2019-07-19 11:46  听雨危楼  阅读(1269)  评论(0编辑  收藏  举报