列表是python中最基础,最强大的数据结构之一。python的列表可以容纳任何东西,其中的元素可以没有任何关系。python的列表就像一个可以容纳万物的容器。但是需要注意,这个容器中的数据是有序的。 在Python 中,用方括号([])表示列表,并用逗号分隔其中的元素。下面是一个例子:
my_list = [1, 'X', [1, 'x']]
print(my_list)
尝试运行它,可以得到如下的结果:
python打印列表的时候,会将外面的方括号打印出来。既然列表是有序的,那么python肯定提供了有序访问的方式,那就是使用下标来进行索引。如下所示:
my_list = [1, 'X', [1, 'x']]
print(my_list) # 打印整个列表
print(my_list[0]) # 打印列表中的第一个元素
print(my_list[1]) # 打印列表中的第二个元素
print(my_list[2]) # 打印列表中的第三个元素
print(my_list[2][0]) # 打印列表中第三个元素,不过由于第三个元素也是列表,因此my_list[2][0]将会打印子列表中的第一个元素
print(my_list[2][1]) # 同理,这将打印子列表中第二个元素
输出的结果如下所示:
python和大多数的计算机编程语言一样,索引是从0开始,而不是1。但是python为列表提供了从尾部开始访问的方式,即使你不知列表有多长。python中可以使用下标-1来索引列表的最后一个元素。下面的例子非常值得认真思考。
my_list = [1, 'X', [1, 'x']]
print(my_list[-1]) # 打印最后一个元素
print(my_list[-2])
print(my_list[-3])
print(my_list[-1][0]) # 打印子列表的第一个元素
print(my_list[-1][-2])
print(my_list[-1][1]) # 打印子列表的第二个元素
print(my_list[-1][-1])
这段代码执行的结果如下所示,对于这个结果,只要你仔细看注释,自己跑一跑代码,应该很容易就可以理解。
修改列表中某个元素的值和使用某个元素的值使用的方式是一样的,如下所示:
my_list[0] = "1" #使用下标索引来修改列表中第一个元素的值。
这种方式和C语言中修改数字中某个元素的值是非常类似的做法,不过列表中的元素的类型是可以是任意的,不像数组。这正是python的强大之处。同时列表还提供了强大的扩充能力,你可以随时向列表中添加新元素。你可以使用append()方法在列表末尾添加新元素,或者是使用insert()方法在列表的任何位置添加新元素。虽然insert()方法的功能包含了append的功能,但是由于我们会频繁的使用追加功能,单独有一个append()函数是更好的,当然了,这两种方法的效率肯定是不同的,因为列表是动态的。我们暂时不深究这个问题。
my_list.append("Hello World") # 在列表末尾追加Hello World
name = "Nicholas Zhao si"
my_list.append(name) # 在列表末尾追加变量name的值
age = 22
my_list.insert(-1, age) # 在列表的最后一个元素之前插入变量age的值
print(my_list)
输出结果如下所示:
有增加,那么必然有删除,删除一个元素的方法有很多种,下面来一一介绍。
首先是del语句,这是一个语句,不是列表这个数据结构专有的删除方式,它可以用在很多地方。下面是一个例子,我们首先来尝试删除name这个变量,看看能否删除掉。
del name
print(name)
程序执行结果如下所示,可以看到del语句是python中通用的删除语句,而不仅仅是在列表中可以使用。
这个del其实是很有意思的东西,它删除的是变量,而不是变量所引用内存中的数据。就相当于删除了某块内存上的一个引用计数。这个问题之后有时间深入python的时候在讨论。其实现在也可以小小的展示一下del的做法。可以看下面这个例子。在此之前,需要介绍一个函数id()。它的作用是一下几点。
id()函数返回指定对象的唯一 id。
Python 中的所有对象都有其自己的唯一 id。
id 在创建时已分配给对象。
id 是对象的内存地址,并且在每次运行程序时都不同。
我打算用id()函数来取地址,从而展示del的做法。id()函数的用法也很简单id(object)即可。例子如下所示:
name_1 = "Nicholas Zhao si" # name_1和name_2是对同一块内存的引用计数。
name_2 = "Nicholas Zhao si"
'''
id()函数返回指定对象的唯一 id。
Python 中的所有对象都有其自己的唯一 id。
id 在创建时已分配给对象。
id 是对象的内存地址,并且在每次运行程序时都不同。
'''
print(id(name_1))
print(id(name_2))
del name_1
print(id(name_2))
我们来看打印结果,如下所示:
可以看到name_2变量的地址依旧没有变化,那就说明del只是删除变量,并不去管内存中的值。删除了name_1这个标记之后,你就不能再使用name_1这个标记了。好了,言归正传,来使用del删除列表中的某个元素。可以看下面的例子。
print(my_list)
del my_list[0]
print(my_list)
程序运行结果如下所示:
python的列表本身还提供了pop方法来进行删除,这方法听起来很像是栈的方法,但是实际上它可以删除非栈顶元素。这个方法的好处是,它将元素弹出以后,你还可以使用这个元素。例如:
top = my_list.pop() # 默认弹出列表中最后一个元素
print(top)
print(my_list)
top = my_list.pop(-2) # 指定位置进行弹出
print(top)
print(my_list)
程序执行结果如下所示:
直接借用书上的一句话来说明什么时候用del,什么时候用pop()方法。
如果你要从列表中删除一个元素,且不再以任何方式使用它,就使用 del 语句;如果你要在删除元素后还能继 续使用它,就使用方法 pop()。
有时候,我们并不知道列表中删除元素的位置,只知道值,那么怎么删除它,先遍历列表,找到位置在删除吗?还好python提供了remove()方法,让事情变得更加简单。下面使用remove方法来看看。
print(my_list)
my_list.remove(22)
print(my_list)
执行代码,结果如下所示:
方法 remove()只删除第一个指定的值。如果要删除的值可能在列表中出现多次,就需要使用循环来确保将每个值都删除。
关于删除还需要说明的一点的如下所示:
guest_list = ["尼古拉斯·赵四","伊丽莎白·翠花"]
print(guest_list)
del guest_list[0] #本来两个,删除一个以后就剩一个了,所以下一个删除也是删除0位置的元素
del guest_list[0]
print(guest_list)
执行结果如下所示:
通常,我们还需要对列表中的元素进行排序操作,以实现一些算法。很多算法都要求对一个有序的排列进行操作。所以排序是很常见的操作。python的列表提供了sort()方法来完成永久性排序(即排序结果会作用于列表本身)。
num_list = [5,2,4,7,3,6,8,1,9,0]
print(num_list)
num_list.sort() # 进行排序,默认数字从小到达排序,默认字母从a-z排序
print(num_list)
num_list.sort(reverse=True) # 设置reverse=True可以让排序结果翻转
print(num_list)
程序执行的结果如下所示:
当然了,有时候并不想使排序的结果作用到列表本身,那么可以使用sorted()方法来实现。
num_list = [5,2,4,7,3,6,8,1,9,0]
print(sorted(num_list)) # 临时排序
print(num_list) # 并不影响num_list本身
print(sorted(num_list,reverse=True)) #sorted函数也可以使用reverse参数来设置排序的方式
程序执行结果如下所示:
常用的方法还有翻转列表,下面的代码展示了如何翻转列表。
num_list = [5,2,4,7,3,6,8,1,9,0]
num_list.reverse() # 翻转列表,效果将作用于列表本身
print(num_list)
num_list.reverse() # 将列表翻转回来
print(num_list)
程序执行的结果如下所示:
当然了,还有计算一个列表长度的函数len(),这个函数可能是非常常用的函数之一。
num_list = [5,2,4,7,3,6,8,1,9,0]
print(len(num_list))
我们可以试试这个函数的效果,如下所示:
虽然python的列表可以容纳任何东西,但是更一般的场景是只容纳某一种类型的数据。例如:容纳整形数字或者是字符串。下面,我将介绍在python种非常重要的函数range()方法。在学习这个函数之前,需要先知道for循环。使用range函数能够很容易生成一堆数字。例如下面的代码:
for num in range(1,20):
print(num)
这段代码的输出结果如下所示:
注意,20并没有被打印。这其实也符合一贯的差一设计。range()函数原型:range(start, stop, step),第一个参数是起始数字,第二个参数是终止数值,第三个参数设置步进。下面是更多的例子,来展示range的用法。
for i in range(1, 10, 2): # 步进是2,因此产生数字:1 3 5 7 9
print(i)
print("\n")
for i in range(5): # 一个参数的时候,默认从0开始,到5终止,因此打印0 1 2 3 4
print(i)
print("\n")
for i in range(0, -10, -2): # 步进是-2
print(i)
这段代码的执行结果如下所示:
下面使用list()函数来配合range()函数来进行数值列表的生成。
numbers = list(range(1, 11)) # 创建1到10的列表
for number in numbers: # 循环打印
print(number)
程序执行结果如下所示:
python还提供了全局的max,min,sum函数来方便我们使用。代码如下所示:
numbers = list(range(1, 11))
for number in numbers:
print(number)
print("最小值:",min(numbers))
print("最大值:",max(numbers))
print("总和:",sum(numbers))
程序执行结果如下所示:
前面的生成列表的代码还可以进一步简化,python提供了列表解析,可以一句话生成数值列表。
numbers = [value for value in range(1, 11)]
# 很好理解, for value in range(1, 11)看作一个整体,每次循环,value的值不同,number列表使用for循环之前的value来生成。
print(numbers)
程序执行结果,直接生成了一个列表。
切片这个操作是python提供的强大机制之一。他能让你几乎随心所欲的访问列表中的元素,以你想要的方式来。下面直接上代码来看一看,这个机制的强大之处。
numbers = list(range(1, 11))
print(numbers[0:5]) # 从number[0]到numbers[4]为止,不打印numbers[5]
print(numbers[4])
print(numbers[5])
程序执行结果如下所示:
有了切片,你想从第几个开始访问,就从第几个开始访问;你还可以想在第几个停止就在第几个停止。这个行为看起来就是给列表进行切片,而且是你想怎么切就怎么切的那种。切片也遵循差一原则,切片的结束位置不会被访问到。我们也可以设置切片的步长,例如:
numbers = list(range(1, 11))
print(numbers[0:10:2]) # 切片其实位置是0,结束位置是10,步长设置为2
print(numbers[0::2]) # 切片可以不设置结束位置,默认就是列表的结束位置,步长设置为2
print(numbers[:10:2]) # 切片也可以不设置起始位置,默认是列表开始的位置。步长设置为2
print(numbers[::2]) # 切片的起始和终止位置都可以没有,只设置步长为2
程序执行结果如下所示:
切片还可以像下面这样使用。
numbers = list(range(1, 11))
print(numbers[0:-1]) # 打印整个列表(除了最后一个元素),默认步长是1
print(numbers[11:0:-2]) # 步长可以是负数,在切片的时候,索引可以超出范围,但是不会报错。
print(numbers[-3:]) # 打印列表中最后三个元素,因为默认终止位置是-1,默认的步长是1
print(numbers[0:]) # 不设置终止位置的时候,可以打印整个列表。一般而言这种方式并没有什么用,如果是这种情形,你应该直接使用整个列表
这段代码的执行结果如下所示:
python中有一点,之前一直都没有感受到,那就是可变类型对象都是引用赋值。这就造成了下面的尴尬场景。代码如下:
numbers = [num for num in range(1, 11)]
numbers_1 = numbers
numbers.insert(0, 123)
print(numbers)
print(numbers_1)
程序执行的结果,是出乎我意料的。
也就是这个numbers和numbers_1是相同的东西。它们指向同一块内存空间。当初在使用整形,字符串,浮点数的时候没有发现这个问题,或者说对这个问题不这么敏感。这是因为这些类型的变量都只能容纳一个值,之后的操作改变了这个值,那么变量就会立即指向另外一块内存空间。所以这个问题很操蛋。下面的代码演示了可变类型对象都是引用赋值的。
str_1 = "abc"
str_2 = "abc"
print(id(str_1)) # id函数打印内存地址
print(id(str_2))
str_3 = str_1
print(id(str_3))
str_3 = "456" # str_3指向另外一块内存空间
print(id(str_3))
# python没有字符,只有字符串。字符串是不可变对象,我们能使用下标读取str_1中的字符,但是没有办法修改它!
print(str_1[1])
程序执行结果如下:
说了这么多,只是想说,在复制列表的时候,看起来只能使用其他方式了,还好python提供的切片成功满足了我们的这个需求。
numbers = list(range(1, 11))
print(numbers)
numbers_1 = numbers[:] # 复制一个列表
print(numbers_1)
numbers.append(10010)
numbers_1.append(10086)
print(numbers)
print(numbers_1)
程序执行结果如下所示,满足我们的预期。