数据结构是以某种方式组合起来的数据元素(如数字、字符乃至其他数据结构)集合。在Python中,最基本的序列(sequence)数据结构为列表(list)和元组(tuple)。列表中的每个元素都有编号,即位置或索引,其中第一个元素的索引为0,第二个元素的索引为1,依次类推,同时列表中也可以嵌套列表,形成更复杂的数据结构。元组是一种特殊的序列,类似于列表,只是不能修改。
”列表“是一个值,包含多个字构成的序列。”列表值“指的是列表本身(作为一个值,可以保存在变量中,或传递给函数,像所有其他值一样),而不是指列表之内的那些值。列表用左方括号开始,用右方括号结束,即[]。列表中的值也称为”表项“,表项用逗号分隔。
>>> [1, 2, 3]
[1, 2, 3]
>>> ['cat', 'bat', 'rat', 'elephant']
['cat', 'bat', 'rat', 'elephant']
>>> ['hello', 3.1415, True, None, 42]
['hello', 3.1415, True, None, 42]
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam
['cat', 'bat', 'rat', 'elephant']
可用下表索引列表中的值,假定列表['cat', 'bat', 'rat', 'elephant']
保存在spam
中,spam[0]
的值为cat
,spam[1]
的值为bat
,依次类推。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[0]
'cat'
>>> spam[1]
'bat'
>>> spam[2]
'rat'
>>> spam[3]
'elephant'
>>> ['cat', 'bat', 'rat', 'elephant'][3]
'elephant'
>>> 'Hello ' + spam[0]
'Hello cat'
>>> 'The ' + spam[1] + ' ate the ' + spam[0] + '.'
'The bat ate the cat.'
如果使用的下标超出了列表中值的个数,Python将给出IndexError出错信息。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[10000]
Traceback (most recent call last):
File "" , line 1, in <module>
spam[10000]
IndexError: list index out of range
下标只能是整数,不能是浮点值。下面的例子将导致TypeError错误。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[1]
'bat'
>>> spam[1.0]
Traceback (most recent call last):
File "" , line 1, in <module>
spam[1.0]
TypeError: list indices must be integers, not float
>>> spam[int(1.0)]
'bat'
列表也可以包含其他列表值。这些嵌套列表中的值,可以通过多重下标来访问。第一个下标表明使用哪个列表值,第二个下标表明该列表值中的值。如果只使用一个下标,程序将打印出该下标处的完整列表值。
>>> spam = [['cat', 'bat'], [10, 20, 30, 40, 50]]
>>> spam[0]
['cat', 'bat']
>>> spam[0][1]
'bat'
>>> spam[1][4]
50
虽然下标从0开始并向上增长,但也可以用负整数作为下标。整数值−1指的是列表中的最后一个下标,−2指的是列表中倒数第二个下标,依次类推。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[-1]
'elephant'
>>> spam[-3]
'bat'
>>> 'The ' + spam[-1] + ' is afraid of the ' + spam[-3] + '.'
'The elephant is afraid of the bat.'
就像下标可以从列表中取得单个值一样,“切片”可以从列表中取得多个值,结果是一个新的列表。切片输入在一对方括号中,但有两个冒号分隔的整数。在一个切片中,第一个整数是切片开始处的下标,第二个整数是切片结束处的下标。切片向上增长,直至第二个下标的值,但不包括他。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[0:4]
['cat', 'bat', 'rat', 'elephant']
>>> spam[1:3]
['bat', 'rat']
>>> spam[0:-1]
['cat', 'bat', 'rat']
省略第一个下标相当于使用0,或列表的开始。省略第二个下标相当于使用列表的长度,意味着分片直至列表的末尾。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[:2]
['cat', 'bat']
>>> spam[1:]
['bat', 'rat', 'elephant']
>>> spam[:]
['cat', 'bat', 'rat', 'elephant']
len()
函数将返回传递给它的列表中值的个数,就像它能计算字符串中字符的个数一样。
>>> spam = ['cat', 'dog', 'moose']
>>> len(spam)
3
一般情况下,赋值语句左边是一个变量名,就像spam = 4
。但是,也可以使用列表的下标来改变下标处的值。例如,spam[1] = 'aardvark'
意味着将列表spam下标1处的值赋值为字符串'aardvark'
。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam[1] = 'aardvark'
>>> spam
['cat', 'aardvark', 'rat', 'elephant']
>>> spam[2] = spam[1]
>>> spam
['cat', 'aardvark', 'aardvark', 'elephant']
>>> spam[-1] = 12345
>>> spam
['cat', 'aardvark', 'aardvark', 12345]
+
操作符可以连接两个列表,得到一个新列表,就像它将两个字符串合并成一个新字符串一样。*
操作符可以用于一个列表和一个整数,实现列表的复制。
>>> [1, 2, 3] + ['A', 'B', 'C']
[1, 2, 3, 'A', 'B', 'C']
>>> ['X', 'Y', 'Z'] * 3
['X', 'Y', 'Z', 'X', 'Y', 'Z', 'X', 'Y', 'Z']
>>> spam = [1, 2, 3]
>>> spam = spam + ['A', 'B', 'C']
>>> spam
[1, 2, 3, 'A', 'B', 'C']
del
语句将删除列表中下标处的值,表中被删除值后面的所有值,都将向前移动一个下标。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> del spam[2]
>>> spam
['cat', 'bat', 'elephant']
>>> del spam[2]
>>> spam
['cat', 'bat']
del
语句也可用于一个简单变量,删除它,作用就像是“取消赋值”语句。如果在删除之后试图使用该变量,就会遇到NameError
错误,因为该变量已不再存在。在实践中,你几乎永远不需要删除简单变量。del
语句几乎总是用于删除列表中的值。
遍历列表有4种方法,常用的是第1种和第2种。
spam = ['cat', 'bat', 'rat', 'elephant']
# 使用range和列表长度进行遍历
for i in range(len(spam)):
print(spam[i])
输出:
cat
bat
rat
elephant
# 利用列表本身进行遍历
for i in spam:
print(i)
输出:
cat
bat
rat
elephant
# 使用迭代器遍历列表
for i in iter(spam):
print(i)
输出:
cat
bat
rat
elephant
# 使用函数enumerate遍历列表,输出的结果是一个元组,包含元素的下标和值
for i in enumerate(spam):
print(i)
输出:
(0, 'cat')
(1, 'bat')
(2, 'rat')
(3, 'elephant')
利用in
和not in
操作符,可以确定一个值是否在列表中。这些表达式将求值为布尔值。
>>> 'howdy' in ['hello', 'hi', 'howdy', 'heyas']
True
>>> spam = ['hello', 'hi', 'howdy', 'heyas']
>>> 'cat' in spam
False
>>> 'howdy' not in spam
False
>>> 'cat' not in spam
True
多重赋值技巧是一种快捷方式,让你在一行代码中,用列表中的值为多个变量赋值。所以不必像下面这样。
>>> cat = ['fat', 'black', 'loud']
>>> size = cat[0]
>>> color = cat[1]
>>> disposition = cat[2]
而是输入下面的代码。
>>> cat = ['fat', 'black', 'loud']
>>> size, color, disposition = cat
>>> cat
['fat', 'black', 'loud']
变量的数目和列表的长度必须严格相等,否则Python将给出ValueError
错误。
>>> cat = ['fat', 'black', 'loud']
>>> size, color, disposition, name = cat
Traceback (most recent call last):
File "" , line 1, in <module>
size, color, disposition, name = cat
ValueError: need more than 3 values to unpack
列表有一组方法,可以进行查找、添加、删除或操作列表中的值。
列表值有一个index()
方法,可以传入一个值,如果该值存在于列表中,就返回它的下标。如果该值不在列表中,Python就报ValueError
。
>>> spam = ['hello', 'hi', 'howdy', 'heyas']
>>> spam.index('hello')
0
>>> spam.index('heyas')
3
>>> spam.index('howdy howdy howdy')
Traceback (most recent call last):
File "" , line 1, in <module>
spam.index('howdy howdy howdy')
ValueError: 'howdy howdy howdy' is not in list
如果列表中存在重复的值,就返回它第一次出现的下标。
>>> spam = ['Zophie', 'Pooka', 'Fat-tail', 'Pooka']
>>> spam.index('Pooka')
1
使用append()
方法可以向列表末尾追加新值,该方法的返回值是None
。
>>> spam = ['cat', 'dog', 'bat']
>>> spam.append('moose')
>>> spam
['cat', 'dog', 'bat', 'moose']
insert()
方法可以在列表任意下标处插入一个值。insert()
方法的第一个参数是新值的下标,第二个参数是要插入的新值,该方法的返回值是None
。
>>> spam = ['cat', 'dog', 'bat']
>>> spam.insert(1, 'chicken')
>>> spam
['cat', 'chicken', 'dog', 'bat']
append()
和insert()
方法是列表方法,只能在列表上调用,不能在其他值上调用。若在字符串和整型上调用将产生AttributeError
错误信息。
>>> eggs = 'hello'
>>> eggs.append('world')
Traceback (most recent call last):
File "" , line 1, in <module>
eggs.append('world')
AttributeError: 'str' object has no attribute 'append'
>>> bacon = 42
>>> bacon.insert(1, 'world')
Traceback (most recent call last):
File "" , line 1, in <module>
bacon.insert(1, 'world')
AttributeError: 'int' object has no attribute 'insert'
remove()
方法会删除列表中的元素。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam.remove('bat')
>>> spam
['cat', 'rat', 'elephant']
试图删除列表中不存在的值,将导致ValueError
错误。
>>> spam = ['cat', 'bat', 'rat', 'elephant']
>>> spam.remove('chicken')
Traceback (most recent call last):
File "" , line 1, in <module>
spam.remove('chicken')
ValueError: list.remove(x): x not in list
如果该值在列表中出现多次,只有第一次出现的值会被删除。
>>> spam = ['cat', 'bat', 'rat', 'cat', 'hat', 'cat']
>>> spam.remove('cat')
>>> spam
['bat', 'rat', 'cat', 'hat', 'cat']
如果知道想要删除的值在列表中的下标,del
语句就很好用。如果知道想要从列表中删除的值,remove()
方法就很好用。
数值列表或字符串列表,可用sort()
方法排序。
>>> spam = [2, 5, 3.14, 1, -7]
>>> spam.sort()
>>> spam
[-7, 1, 2, 3.14, 5]
>>> spam = ['ants', 'cats', 'dogs', 'badgers', 'elephants']
>>> spam.sort()
>>> spam
['ants', 'badgers', 'cats', 'dogs', 'elephants']
也可以指定reverse
关键字参数为True
,让sort()
按逆序排序。
>>> spam.sort(reverse=True)
>>> spam
['elephants', 'dogs', 'cats', 'badgers', 'ants']
使用sort()
时应该注意一下三点。
(1)sort()
方法当场对列表排序,返回值为None
,不要试图记录返回值。
(2)不能对既有数字又有字符串值的列表排序,因为Python不知道如何比较它们。
>>> spam = [1, 3, 2, 4, 'Alice', 'Bob']
>>> spam.sort()
Traceback (most recent call last):
File "" , line 1, in <module>
spam.sort()
TypeError: unorderable types: str() < int()
(3)sort()
方法对字符串排序时,使用“ASCII 字符顺序”,而不是实际的字典顺序。这意味着大写字母排在小写字母之前。
>>> spam = ['Alice', 'ants', 'Bob', 'badgers', 'Carol', 'cats']
>>> spam.sort()
>>> spam
['Alice', 'Bob', 'Carol', 'ants', 'badgers', 'cats']
如果需要按照普通的字典顺序来排序,就在sort()
方法调用时,将关键字参数key
设置为str.lower
。这将导致sort()
方法将列表中所有的表项当成小写,但实际上并不会改变它们在列表中的值。
>>> spam = ['a', 'z', 'A', 'Z']
>>> spam.sort(key=str.lower)
>>> spam
['a', 'A', 'z', 'Z']
字符串也是表示序列的一种数据类型。字符串和列表实际上很相似,只是字符串是单个文本字符的列表。对列表的许多操作,也可以作用于字符串:按下标取值、切片、用于for
循环、用于len()
,以及用于in
和not in
操作符。
>>> name = 'Zophie'
>>> name[0]
'Z'
>>> name[-2]
'i'
>>> name[0:4]
'Zoph'
>>> 'Zo' in name
True
>>> 'z' in name
False
>>> 'p' not in name
False
>>> for i in name:
print('* * * ' + i + ' * * *')
* * * Z * * *
* * * o * * *
* * * p * * *
* * * h * * *
* * * i * * *
* * * e * * *
列表和字符串在一个重要的方面是不同的。列表是“可变的”数据类型,列表的值可以添加、删除或改变。但是字符串是“不可变的”,它不能被更改。尝试对字符串中的一个字符重新赋值,将导致TypeError
错误。
>>> name = 'Zophie a cat'
>>> name[7] = 'the'
Traceback (most recent call last):
File "" , line 1, in <module>
name[7] = 'the'
TypeError: 'str' object does not support item assignment
“改变”一个字符串的正确方式,是使用切片和连接。构造一个“新的”字符串,然后从老的字符串那里复制一些部分。
>>> name = 'Zophie a cat'
>>> newName = name[0:7] + 'the' + name[8:12]
>>> name
'Zophie a cat'
>>> newName
'Zophie the cat'
元组(tuple)的数据类型和列表的数据类型很相似,但有两点不同,第一元组用圆括号(),而不是用方括号[],第二元组和字符串一样,是不可变的数据类型,不能添加、修改或删除,若更改元组,会输出TypeError
错误信息。
>>> eggs = ('hello', 42, 0.5)
>>> eggs[0]
'hello'
>>> eggs[1:3]
(42, 0.5)
>>> len(eggs)
3
>>> eggs = ('hello', 42, 0.5)
>>> eggs[1] = 99
Traceback (most recent call last):
File "" , line 1, in <module>
eggs[1] = 99
TypeError: 'tuple' object does not support item assignment
如果元组中只有一个值,你可以在括号内该值的后面跟上一个逗号,表明这种情况。否则,Python将认为只是在一个普通括号内输入了一个值。逗号告诉Python,这是一个元组。
>>> type(('hello',))
<class 'tuple'>
>>> type(('hello'))
<class 'str'>
正如str(42)
将返回42
,即整数42
的字符串表示形式,函数list()
和tuple()
将返回传递给它们的值的列表和元组版本。
>>> tuple(['cat', 'dog', 5])
('cat', 'dog', 5)
>>> list(('cat', 'dog', 5))
['cat', 'dog', 5]
>>> list('hello')
['h', 'e', 'l', 'l', 'o']
将列表赋给一个变量时,实际上是将列表的“引用”赋给了该变量。引用是一个值,指向某些数据。列表引用是指向一个列表的值。
>>> spam = [0, 1, 2, 3, 4, 5]
>>> cheese = spam
>>> cheese[1] = 'Hello!'
>>> spam
[0, 'Hello!', 2, 3, 4, 5]
>>> cheese
[0, 'Hello!', 2, 3, 4, 5]
变量包含对列表值的引用,而不是列表值本身。但对于字符串和整数值,变量就包含了字符串或整数值。在变量必须保存可变数据类型的值时,例如列表或字典,Python就使用引用。对于不可变的数据类型的值,例如字符串、整型或元组,Python变量就保存值本身。
当函数被调用时,参数的值被复制给变元。对于列表及字典,这意味着变元得到的是引用的拷贝。当eggs()
被调用时,没有使用返回值来为spam
赋新值。相反,它直接修改了原来列表。
def eggs(someParameter):
someParameter.append('Hello')
spam = [1, 2, 3]
eggs(spam)
print(spam)
输出:
[1, 2, 3, 'Hello']
在处理列表和字典时,尽管传递引用常常是最方便的方法,但如果函数修改了传入的列表或字典,你可能不希望这些变动影响原来的列表或字典。要做到这一点,Python提供了名为copy
的模块,其中包含copy()
和deepcopy()
函数。
第一个函数copy.copy()
,可以用来复制列表或字典这样的可变值,而不只是复制引用。spam
拷贝后赋值给cheese
,spam
和cheese
现在是两个变量,修改一个,另外一个的值不变。
>>> import copy
>>> spam = ['A', 'B', 'C', 'D']
>>> cheese = copy.copy(spam)
>>> cheese[1] = 42
>>> spam
['A', 'B', 'C', 'D']
>>> cheese
['A', 42, 'C', 'D']
如果要复制的列表中包含了列表,那就使用copy.deepcopy()
函数来代替,deepcopy()
函数将同时复制它们内部的列表。
import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始对象
b = a #赋值,传对象的引用
c = copy.copy(a) #对象拷贝,浅拷贝
d = copy.deepcopy(a) #对象拷贝,深拷贝
a.append(5) #修改对象a
a[4].append('c') #修改对象a中的['a', 'b']数组对象
print( 'a = ', a )
print( 'b = ', b )
print( 'c = ', c )
print( 'd = ', d )
输出:
('a = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('b = ', [1, 2, 3, 4, ['a', 'b', 'c'], 5])
('c = ', [1, 2, 3, 4, ['a', 'b', 'c']])
('d = ', [1, 2, 3, 4, ['a', 'b']])
>>> spam = [1, ['a', 'b'], 2]
>>> spam1 = copy.copy(spam)
>>> spam1
[1, ['a', 'b'], 2]
# 经过copy的列表,使用append操作列表里面的列表,两个变量都会变化
>>> spam[1].append('c')
>>> spam
[1, ['a', 'b', 'c'], 2]
>>> spam1
[1, ['a', 'b', 'c'], 2]
>>> spam1[1].append('d')
>>> spam1
[1, ['a', 'b', 'c', 'd'], 2]
>>> spam
[1, ['a', 'b', 'c', 'd'], 2]
# 若是直接赋值,则被赋值的会变化,另外一个不会变化,这和python底层数据存储的方式
# 有关。对于列表,python保存的是指向元素的地址,append会修改地址指向元素的值,而
# 赋值会直接改变地址,使得被修改的列表指向新的列表,因此赋值不会影响原来的变量的值。
>>> spam[1] = ['a', 'b']
>>> spam
[1, ['a', 'b'], 2]
>>> spam1
[1, ['a', 'b', 'c', 'd'], 2]