类似于C语言的数组,但是列表可以包含不同类型的任意对象。列表是可变类型。
创建列表——手动赋值、工厂函数:
>>> aList = [12, 'abc']
>>> print aList
[12, 'abc']
>>> list('foo') #参数为可迭代对象
['f', 'o', 'o']
访问列表元素——下标[]或者切片[:]。
更新列表——直接对索引/索引范围赋值:
>>> aList[0] = 'def' #对一个索引赋值
>>> aList
['def', 'abc']
>>> aList[0], aList[1] = 'mn', 'xyz' #对一个索引范围赋值
>>> aList
['mn', 'xyz']
>>> aList[:] = 'ab', 'cd' #列表重新赋值
>>> aList
['ab', 'cd']
>>> aList[:] = 'ab', 'cd', 'ef' #超出原列表长度
['ab', 'cd', 'ef']
>>> aList.append('gh') #append()方法,在后面添加
>>> aList
['ab', 'cd', 'ef', 'gh']
删除列表元素/列表本身:
>>> del aList[2] #根据下标删除
>>> aList
['ab', 'cd', 'gh']
>>> aList.remove('cd') #如果知道元素内容,则直接删除该元素
>>> aList
['ab', 'gh']
>>> del aList #删除列表本身,并不需要显示调用,在程序结束时自会收回内存的
>>> aList
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'aList' is not defined
12 操作符
12.1 标准类型操作符
比较运算:两个列表的元素分别比较,直到有一方的元素胜出。
>>> alist = ['abc', 123]
>>> blist = ['xyz', 789]
>>> clist = ['abc', 123]
>>> alist < blist
True
>>> alist == clist
True
12.2 序列类型操作符
切片操作符[]、成员操作符[not] in、连接操作符+、重复操作符* 都适用于列表。有几个要注意的点:
>>> alist = [1, 2]
>>> alist
[1, 2]
>>> alist[1] = [1, 2] #对某一索引赋值,则新值代替该元素
>>> alist
[1, [1, 2]]
>>> alist[:1] = [1, 2] #对某一范围赋值,则新值直接作为元素覆盖原来范围
>>> alist
[1, 2, [1, 2]]
>>> alist + [3, 4]
[1, 2, [1, 2], 3, 4]
>>> alist #连接操作符并不在原对象上进行修改,且只能和列表对象连接
[1, 2, [1, 2]]
>>> alist.extend('abc') #extend()函数将参数的每个元素作为原对象的元素,参数为可迭代对象
>>> alist
[1, 2, [1, 2], 'a', 'b', 'c']
>>> alist.append('abc') #append()函数将参数整体作为最后一个元素
>>> alist
[1, 2, [1, 2], 'a', 'b', 'c', 'abc']
12.3 列表解析
列表没有自己专门的操作符,但是有自己专属的列表解析:
>>> [ i * 2 for i in [8, -2, 5] ]
[16, -4, 10]
>>> [ i for i in range(8) if i % 2 == 0 ]
[0, 2, 4, 6]
13 内建函数
13.1 标准类型函数
cmp()也可以用于列表比较。比较时逐元素比较,知道比出大小:数字按大小比较,字符串按字符ASCII码值比较,数字永远小于字符串。
13.2 序列类型函数
len()——返回元素个数
max()、min()——返回最大/最小元素值。包含复杂对象时,返回不一定准确。
sorted()、reversed()——返回排序的列表/逆序的迭代对象。字符串排序的时候按照字典序,即ASCII码值顺序。
enumerate()、zip()——同序列中的功能。
sum()——求和。只对整数列表,而且还有个默认参数。
list()、tuple()——将一个可迭代对象转换成一个列表/元组对象。(原对象不变,生成一个新对象)
14 列表类型的内建函数(方法)
Python中没有用于列表的专用内建函数,但是列表类型有自己的类方法。
列表是可变类型,这些方法直接在原列表上进行修改,所以没有返回值(相比字符串就必须返回新对象):
1 list.append(obj) #向列表中添加一个对象obj
2 list.count(obj) #返回一个对象obj在列表中出现的次数
3 list.extend(seq) #把序列seq的元素作为列表元素添加到列表中
4 list.index(obj, i=0, j=len(list)) #返回第一个值为obj的下标,不存在则引发ValueError异常
5 list.insert(index, obj) #在索引值为index的位置插入对象obj
6 list.pop(index=-1) #删除并返回指定位置的对象,默认是最后一个对象
7 list.remove(obj) #从列表中删除对象obj
8 list.reverse() #原地翻转列表
#重排序列表,如果func和key参数指定,则按照指定的方式比较各个元素,如果reverse标志被置为True,则列表以反序排列
9 list.sort(func=None,key=None, reverse=False)
15 列表的特殊特性
列表有容器和可变的特性,可以用来实现其他数据结构——栈、队列。
栈——后进先出:
#!/usr/bin/env python
'my stack'
stack = [] #空列表,用于保存栈元素
def pushit(): #入栈,加到列表尾部
stack.append(raw_input('Enter new string: ').strip())
def popit(): #出栈
if len(stack) == 0:
print 'Cannot pop from an empty stack!'
else:
print 'Removed [', `stack.pop()`, ']' #list.pop()默认弹出最后一个元素,用反引号将其转为字符串
def viewstack(): #打印栈(列表)内容
print stack
CMDs = {'u': pushit, 'o': popit, 'v': viewstack} #字典,值是函数名
def showmenu():
pr = """
p[U]sh
p[O]p
[V]iew
[Q]uit
Enter choice: """
while True: #主循环,根据输入决定流程
while True: #输入循环,判断输入是否正确
try:
choice = raw_input(pr).strip()[0].lower()
#分别输入^D(EOF)、^C(中断退出)、输入为空或只有空字符 时导致的错误
except (EOFError, KeyboardInterrupt, IndexError):
choice = 'q'
print '\nYou picked: [%s]' % choice
if choice not in 'uovq':
print 'Invalid option, try again'
else:
break
if choice == 'q':
break
CMDs[choice]() #根据输入,读字典的值,并调用函数
if __name__ == '__main__': #如果在命令行执行,则if成功;如果作为模块导入,则判断失败
showmenu()
列表——先进先出:
#!/usr/bin/env python
'my queue'
queue = []
def enQ():
queue.append(raw_input('Enter new string: ').strip())
def deQ():
if len(queue) == 0:
print 'Cannot pop from an empty queue!'
else:
print 'Removed [', `queue.pop(0)`, ']' #差别只在于,每次弹出第一个元素
def viewQ():
print queue
CMDs = {'e': enQ, 'd': deQ, 'v': viewQ}
def showmenu():
pr = """
[E]nqueue
[D]equeue
[V]iew
[Q]uit
Enter choice: """
while True:
while True:
try:
choice = raw_input(pr).strip()[0].lower()
except (EOFError, KeyboardInterrupt, IndexError):
choice = 'q'
print '\nYou picked: [%s]' % choice
if choice not in 'devq':
print 'Invalid option, try again'
else:
break
if choice == 'q':
break
CMDs[choice]()
if __name__ == '__main__':
showmenu()
16 元组
元组和列表有很多相似性,不同点在于:元组用圆括号()包围;元组是不可变类型。
创建元组——直接赋值、工厂函数:
>>> atuple = (12, 'abc', [34, 'def'])
>>> atuple
(12, 'abc', [34, 'def'])
>>> btuple = (123) #一个例外:只有一个元素时,要加逗号,否则括号就变为普通的括号
>>> btuple
123
>>> type(btuple)
<type 'int'>
>>> btuple = (123, ) #加逗号表示一个元组
>>> btuple
(123,)
>>> type(btuple)
<type 'tuple'>
>>> tuple('foo') #参数为可迭代对象
('f', 'o', 'o')
访问元素——下标[]或者切片[:]。
更新元组:同字符串一样,元组的内容是不可以改变的,当变量被赋与其它元组时,其实是生成了一个新的元组对象。
删除元组:删除元组元素是不可能的,可以调用del来删除整个元组,原理同删除列表一样。
17 元组操作符和内建函数
标准类型操作符:比较运算符、逻辑运算符都适用于元组,原理同列表。
序列类型操作符:切片操作符[]、成员操作符[not] in、连接操作符+、重复操作符* 都适用于元组。
内建函数:cmp()函数、序列类型函数仍然适用于元组。(sorted()返回的是排序好的列表)
Python没有专门用于元组的操作符和内建函数,元组类型只有两个方法——统计和查找:
1 tuple.count(obj) #返回一个对象obj在元组中出现的次数
2 tuple.index(obj, i=0, j=len(tuple)) #返回第一个值为obj的下标,如果不存在则引发ValueError异常
18 元组的特殊性
18.1 不可变性的影响
切片操作不能作为左值被赋值;被传给其他函数时,可以确保数据不被修改。
18.2 元组的一个“可变性”
如果元组里面有可变类型的元素,那个这个元素的内容还是可以改变的:
>>> t1 = ([1,2], 3)
>>> t1
([1, 2], 3)
>>> t1[0][1] = [4, 'abc']
>>> t1
([1, [4, 'abc']], 3)
18.3 默认集合类型
几个没有明确符号定义的,只是用逗号隔开的对象,默认是一个元组类型:
>>> 1, 3 < 2, 4 #逗号优先级小于比较符号,不加括号带来的副作用
(1, False, 4)
>>> (1, 3) < (2, 4)
True
>>> def foo():
... return rt1, rt2, rt3 #实际返回的是一个元组
18.4 单元素元组
之前已经介绍过,单元素的元组创建时,应该在这个元素后面加一个逗号;以此防止括号被当成分组操作符。
18.5 字典的关键字
字典的原理就是哈希表,其键应该是唯一的,所以它的键必须是不可变类型。
列表VS元组
不可变类型:当需要把一组数据传递给一个陌生函数时,应该使用元组,防止数据被修改。
可变类型:在管理动态数据集时,需要不定期的增删改元素。
转换:list()和tuple()可以将对象在列表和元组之间转换。
19 相关模块
与序列类型相关的模块:
1 数组 #一种受限制的可变序列类型,要求所有的元素必须都是相同的类型
2 copy #提供浅拷贝和深拷贝的能力
3 operator #包含函数调用形式的序列操作符,比如operator.concat(m,n)就相当于连接操作(m+n)
4 re #Perl风格的正则表达式查找(和匹配);见第 15 章
5 StringIO/cStringIO #把长字符串作为文件来操作,比如read()、seek()函数等,C版的更快一些,但是不能被继承
6 textwrap #用作包裹/填充文本的函数,也有一个类
7 types #包含Python支持的所有类型
8 collections #高性能容器数据类型
20 拷贝Python对象
拷贝只发生在容器类型中(即列表、元组等),浅拷贝的概念可以通过以下例子来理解:
>>> aList = ['a', ['aa', 100]]
>>> bList = aList[:] #通过切片操作符来复制列表(按照概念,就是切片操作返回一个新列表对象,然后将其引用赋值给新变量)
>>> cList = list(aList) #通过工厂函数来复制列表
>>> [id(x) for x in aList, bList, cList] #打印三个列表id值,都不一样
[158368652, 158358316, 158212652]
>>> for xList in aList, bList, cList: #打印三个列表的元素值,都一样
... [id(x) for x in xList]
...
[3073138104L, 157929228]
[3073138104L, 157929228]
[3073138104L, 157929228]
通过以上例子可知,浅拷贝的时候,只是创建了一个和被复制对象一样类型的新对象(类型、元素数目、值都一样),而其中的元素还是指向了被复制对象的元素。如果更新某一个对象中的可变类型,会影响到其他的对象。
深拷贝的意思则显而易见:
>>> import copy
>>> dList = copy.deepcopy(aList) #模块里的深拷贝函数
>>> eList = copy.copy(aList) #模块里的浅拷贝函数
>>> [id(x) for x in aList, dList, eList] #仍然生成两个新的列表对象
[158368652, 158368140, 158215884]
>>> for xList in aList, dList, eList:
... [id(x) for x in xList]
...
[3073138104L, 157929228]
[3073138104L, 158358348] #第一个元素都是指向同一个字符串对象,而深拷贝的时候,第二个列表元素则为新创建的列表对象
[3073138104L, 157929228]
总结:
浅拷贝有三种:①切片[:],②工厂函数list()、dict()等、③copy()函数。元组浅拷贝时甚至不生成新对象,直接把被复制对象的引用赋值给新变量。
深拷贝的时候,创建一个新对象,其中的不可变类型元素仍指向被复制对象的相应元素,而可变类型元素则为新创建对象。如果元组中不包含不可变类型的元素,则仍然进行浅拷贝。
21 序列类型小结
序列:顺序存储——字符串、列表、元组。
序列操作符:连接+、重复*、切片[:]、成员[not] in。
方法:seq.method()。
练习题
6-3 排序 (a)输入一串数字,从大到小排列之。 (b)跟a一样,不过要用字典序从大到小排列之。
1 #!/usr/bin/env Python 2 3 dict_sorted_list = [] 4 numbers = raw_input('Enter the numbers separated by space: ').split() 5 6 for x in sorted(numbers, reverse = True): 7 dict_sorted_list.append(int(x)) 8 9 for i,x in enumerate(numbers): 10 numbers[i] = int(x) 11 12 sorted_list = sorted(numbers, reverse = True) 13 14 print 'orignal input:', numbers 15 print ' sorted:', sorted_list 16 print ' dict sorted:', dict_sorted_list