01. 列表
列表
到目前为止,我们一直在处理各种单独的数据,比如字符串和数字。但当我们使用数据集合时,还可以编写出更强大的程序。现在我们先介绍一下第一种数据集合:列表。
python_versions = [1.0, 1.5, 1.6, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6]
上面这行代码定义了一个变量
python_versions
,其中包含一系列浮点数。列表中的每个元素都表示 Python 的一个版本号(Python 的版本一直在升级,因此有很多不同的版本号)。列表使用方括号(
[
和
]
)定义,列表内的元素用逗号分开。
我们可以通过索引来查找列表中的各个元素,比如针对上面这个列表,我们可以按照如下所示的方法查找版本号:
>>> python_versions[0]
1.0
>>> python_versions[1]
1.5
>>> python_versions[7]
2.4
请注意列表中的第一个元素
1.0
的索引编号为 0 ,而不是 1。有许多编程语言都遵循这个惯例,我们将其称为"零索引"。如果这种说法让你难以理解,你也可以这样理解:元素的索引编号代表元素与列表开头的距离。第一个元素距离开头有 0 个元素,第二个元素有一个元素,以此类推。
当然,除了这种从列表开头进行索引的方法,我们也可以从列表的末尾进行索引。
要从列表的末尾索引需要使用负索引。以我们在上方定义的列表为例,我们可以通过下面这种方法得到最新的 Python 版本:
>>> python_versions[-1]
3.6
索引
-1
是指列表的最后一个元素,
-2
是倒数第二个,以此类推。
索引错误
如果尝试索引列表中不存在的元素,将导致列表索引异常(List Index Exception)。这条信息表示 Python 提醒你正在尝试访问一个列表中不存在的元素。
例如,我们定义以下列表:
>>> my_list = ['a','b','c','d','e']
这个列表中有五个元素,索引编号分别是 0、1、2、3 和 4
>>> my_list[4]
'e'
如大家所见,
my_list [4]
返回这个列表的最后一个元素。但是当我们尝试访问索引编号为 5 的元素时,会出现什么结果?
>>> my_list[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
因为列表中只包含 5 个元素,尝试访问索引编号为 5 的元素其实是要求 Python 给出该列表中的第 6 个元素。由于这个元素并不存在,所以导致了一个
IndexError
。
索引错误十分常见,尤其是当你还不习惯在 Python 中进行索引的时候。刚开始你可能会经常收到这些错误信息,但在慢慢熟悉 Python 后,这样的错误就会很少出现了。导致索引错误最常见的一个原因是在进行索引时编号差 1(比如在上方的例子中,
my_list[5]
会导致索引错误,因为列表中最后一个元素的编号实际上是 4),但也存在一些其他原因。因此我们建议你使用
print
函数来打印你想要索引的元素,以此进行纠错,这可以提醒你的索引是否有差,差是多少。
练习:列表索引
请完成函数
how_many_days
,其将输入一个表示月份的数字,并返回该月份的天数。我们定义的
days_in_month
是一个包含各月天数的列表。例如,
how_many_days(8)
应该返回 31,因为第八个月,即八月,有 31 天。
记住索引编号从零开始!
(提示:目前函数还没有学过,你可以参考下一课: 函数 来了解一些基本知识。本节中编程习题的解决方案可以在下一小节: 解决方案:列表 中查看。)
Start Quiz:
def how_many_days(month_number):
"""Returns the number of days in a month.
WARNING: This function doesn't account for leap years!
"""
days_in_month = [31,28,31,30,31,30,31,31,30,31,30,31]
#todo: return the correct value
# This test case should print 31, the number of days in the eighth month, August
print(how_many_days(8))
列表切片
除了从列表访问各个元素外,我们还可以使用 Python 的切片符号来访问列表的子序列。大家来看一下这个月份列表,
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
我们可以从月份列表中切片出一年的第三季度,如下所示:
>>> q3 = months[6:9]
>>> print(q3)
['July', 'August', 'September']
>>> print(months)
['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
![`q3 = months[6:9]`](img/slicing.png)
q3 = months[6:9]
冒号左侧的索引编号 6 是切片开始的位置。切片持续到第二个索引编号 9(请注意,切片不包括索引编号为 9 的元素,但包括编号为 6 的元素,以此类推)。
切片简化方式
当然我们也有一些简化切片代码的方法。如果你想获得一个从原始列表开头开始的子列表,或者一个在原始列表的末尾结束的子列表,可以采用如下所示的方法来快捷开始或结束索引:
>>> first_half = months[:6]
>>> print(first_half)
['January', 'February', 'March', 'April', 'May', 'June']
>>> second_half = months[6:]
>>> print(second_half)
['July', 'August', 'September', 'October', 'November', 'December']
练习:列表切片
请使用列表切片符号从下面这个列表中选择三个最近的日期。提示:负索引也适用于列表切片。
Start Quiz:
eclipse_dates = ['June 21, 2001', 'December 4, 2002', 'November 23, 2003',
'March 29, 2006', 'August 1, 2008', 'July 22, 2009',
'July 11, 2010', 'November 13, 2012', 'March 20, 2015',
'March 9, 2016']
# TODO: Modify this line so it prints the last three elements of the list
print(eclipse_dates)
列表、字符串和可变性
与
string
、
float
和
int
一样,
list
也是一种类型。在我们看到的所有类型中,列表与字符串最为相似:这两种类型都支持索引、切片、
len
函数和
in
运算符。
>>> sample_string = "And Now For Something Completely Different"
>>> sample_list = ['Graham', 'John', 'Terry', 'Eric', 'Terry', 'Michael']
>>> sample_string[4]
'N'
>>> sample_list[4]
'Terry'
>>> sample_string[12:21]
'Something'
>>> sample_list[2:4]
['Terry', 'Eric']
>>> len(sample_string)
42
>>> len(sample_list)
6
>>> 'thing' in sample_string
True
>>> 'Rowan' in sample_list
False
那么列表与字符串有什么不同?其中最明显的区别是字符串为字母序列,而列表的元素可以是 任何 类型的对象。更细微的区别是列表可以被修改,但字符串不能:
>>> sample_list[3] = 'Eric'
>>> print(sample_list)
['Graham', 'John', 'Terry', 'Eric', 'Terry', 'Michael']
>>> sample_string[8] = 'f'
TypeError: 'str' object does not support item assignment
表示对象可否修改的术语是 可变性 (Mutability)。列表是可变的,而字符串不可变。接下来,我们将探讨可以在列表中使用的方法和函数,同时将在程序中利用列表的可变性。
可变性匹配练习
QUIZ QUESTION: :
假设我们有以下两个表达式,
sentence1
和
sentence2
:
sentence1 = "I wish to register a complaint."
sentence2 = ["I", "wish", "to", "register", "a", "complaint", "."]
将下面的 python 代码与修改后的
sentence1
或
sentence2
值相匹配。如果代码导致错误,则与
Error 匹配。
ANSWER CHOICES:
Python code |
|
---|---|
["Our Majesty", "wish", "to", "register", "a", "complaint", "."] |
|
["I", "wish", "to", "register", "a", "complaint", "!"] |
|
“I wish to register a complaint!” |
|
["We", "want", "to", "register", "a", “complaint”, "."] |
|
Error |
SOLUTION:
Python code |
|
---|---|
["Our Majesty", "wish", "to", "register", "a", "complaint", "."] |
|
["I", "wish", "to", "register", "a", "complaint", "!"] |
|
["We", "want", "to", "register", "a", “complaint”, "."] |
|
Error |
保存列表的变量
之前,当创建一个具有不可变对象的变量时,该不可变对象的值即被保存在内存中。例如在下面这个示例中,我们创建了一个值为
"Old Woman"
的变量
name
,并将其赋值给另一个变量
person
。
>>> name = "Old Woman"
>>> person = name
>>> name = "Dennis"
>>> print(name)
Dennis
>>> print(person)
Old Woman
在第二行代码中,字符串 "Old Woman" 已经为
person
赋值。因此当我们在后面为
name
重新赋值,将其更新为
"Dennis"
时,并不会影响到
person
的值。
列表与字符串不同,它们是可变的。在下面这个示例中,我们创建了一个名为
dish
的列表,列表中包含了一家咖啡厅的菜肴。我们将这个列表赋值给变量
mr_buns_order
。当我们由于一种食材不可用而更改(转变)列表
dish
时,会同时影响
dish
和
mr_buns_order
:
>>> dish = ["Spam", "Spam", "Spam", "Spam", "Spam", "Spam", "baked beans", "Spam", "Spam", "Spam", "Spam"]
>>> mr_buns_order = dish
>>> print(dish)
['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'baked beans', 'Spam', 'Spam', 'Spam', 'Spam']
>>> print(mr_buns_order)
['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'baked beans', 'Spam', 'Spam', 'Spam', 'Spam']
>>> dish[6] = "Spam" #baked beans are off
>>> print(mr_buns_order)
['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
>>> print(dish)
['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
dish
和
mr_buns_order
是同一底层列表的两个变量名,我们可以使用任一名称来访问和更改该列表。
包含可变和不可变对象的变量运行方式非常不同,你必须十分注意这一点。
你需要不断进行试验,通过
print
函数来检查你的代码,以确保程序可以正确处理数据。
使用列表
下面是一些可以与列表一起使用的函数:
len(some_list)
返回
some_list
中的元素个数
max(some_list)
返回列表中的最大元素。最大元素的确定取决于列表中的对象类型。数字列表中的最大元素是最大的数字:
>>> batch_sizes = [15, 6, 89, 34, 65, 35]
>>> max(batch_sizes)
89
字符串列表的最大元素是首字母顺序排在最后的一个元素:
>>> python_varieties = ['Burmese Python', 'African Rock Python', 'Ball Python', 'Reticulated Python', 'Angolan Python']
>>> max(python_varieties)
'Reticulated Python'
这是因为
max
函数采用比较运算符
>
定义。有许多非数字类型都可以使用
>
运算符进行比较。如果正在处理的对象可以用
>
比较,那么在这类对象的列表中便可以使用
max
函数。对于字符串来说,比较标准是首字母顺序,因此上面示例中列表的最大值就是首字母顺序排在最后的元素。
当一个列表中包含不同类型的元素,并且这些类型无法进行比较时,
max
函数也将无法使用:
>>> max([42, 'African Swallow'])
TypeError: unorderable types: str() > int()
这是因为
max
函数采用
>
定义,如果无法比较列表中的两个对象,则无法确定最大元素。
min(some_list)
返回列表中的最小元素。
min
与
max
相反。
sorted(some_list)
按从小到大的顺序返回
some_list
的副本,同时保持
some_list
不变。可以通过添加可选参数
reverse = True
按从大到小的顺序排序。
>>> sorted(batch_sizes)
[6, 15, 34, 35, 65, 89]
>>> sorted(batch_sizes, reverse=True)
[89, 65, 35, 34, 15, 6]
>>> print(batch_sizes)
[15, 6, 89, 34, 65, 35]
连接列表
下面我们来介绍一个新的字符串方法
join
,使用示例如下:
>>> nautical_directions = "\n".join(["fore", "aft", "starboard", "port"])
>>> print(nautical_directions)
fore
aft
starboard
port
join 将一个列表作为参数,返回一个由分隔符字符串连接列表元素组成的字符串。在这个示例中,我们使用字符串
\n
作为分隔符,以便使每个元素之间有一个换行符。

我们也可以和
.join
配合使用其他字符串(而不是
'\n'
)。例如:
>>> names = ["García", "O'Kelly", "Davis"]
>>> "-".join(names)
"García-O'Kelly-Davis"
注意,务必用逗号 (
,
) 隔开连接列表中的每个元素。如果忘记隔开,尽管不会导致错误,但也会使你无法获得理想的结果。下面的例子中,"García” 和 "O'Kelly” 之间没有逗号,所以出现了以下结果:
>>> names = ["García" "O'Kelly", "Davis"]
>>> "-".join(names)
"GarcíaO'Kelly-Davis"
你有没有注意到 "García" 和 "O'Kelly" 之间的 '-' 分隔符消失了?这是因为 Python 默认按字符串的字面形式进行连接。如果
.join
返回的结果与你的预期不同,那么你最好检查是否丢失了逗号。
你还要注意的是,如果你尝试在列表中加入字符串以外的其他任何内容,
join
会触发错误。例如:
>>> stuff = ["thing", 42, "nope"]
>>> " and ".join(stuff)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence item 1: expected str instance, int found
追加到列表
列表对象的
append
方法在列表末尾添加一个元素。
>>> python_varieties.append('Blood Python')
>>> print(python_varieties)
['Burmese Python', 'African Rock Python', 'Ball Python', 'Reticulated Python', 'Angolan Python', 'Blood Python']
练习:前三名
编写一个函数
top_three
,该函数以列表为参数,返回三个最大元素的列表。例如,
top_three([2,3,5,6,8,4,2,1]) == [8, 6, 5]
Start Quiz:
def top_three(input_list):
"""Returns a list of the three largest elements input_list in order from largest to smallest.
If input_list has fewer than three elements, return input_list element sorted largest to smallest/
"""
# TODO: implement this function
练习:中位数(Median)
此练习中的函数
median
返回输入列表的中值。但该函数只适用于具有奇数个元素的列表。现在请你修改函数,当为
median
输入具有偶数个元素的列表时,该函数可返回两个中心元素的平均值。练习中提供的用例可以使你测试预期结果。
Start Quiz:
def median(numbers):
numbers.sort() #The sort method sorts a list directly, rather than returning a new sorted list
middle_index = int(len(numbers)/2)
return numbers[middle_index]
test1 = median([1,2,3])
print("expected result: 2, actual result: {}".format(test1))
test2 = median([1,2,3,4])
print("expected result: 2.5, actual result: {}".format(test2))
test3 = median([53, 12, 65, 7, 420, 317, 88])
print("expected result: 65, actual result: {}".format(test3))
单击“下一项”查看该练习的解决方案。