元组
:相对简单,是str的扩展,与字符串一样,是一些元素的不可变有序序列。与字符串的区别是,元组(tuple)中的元素不一定是字符,其中的单个元素可以是任意类型,且它们彼此之间的类型也可以不同。
# tuple类型的字面量形式是位于小括号之中的由逗号隔开的一组元素
t1=()
t2 = (1,) #注意当元组中只有一个元素时,加逗号
t3 = (1, 'two', 3)
print(t1,t2,t3)
() (1,) (1, 'two', 3)
#可以在元组上使用重复操作。
print(3*('a', 2))
#与字符串一样,元组可以进行连接、索引和切片等操作。
t1 = (1, 'two', 3)
t2 = (t1, 3.25)#,这个元组中有一个绑定了名称t1的元组和一个浮点数3.25。这是可行的,因为元组也是一个对象
print(t2)
print((t1 + t2))
print((t1 + t2)[3])
print((t1 + t2)[2:5])
('a',2,'a',2,'a',2)
((1,'two',3),3.25)
(1,'two',3,(1,'two',3),3.25)
(1,'two',3)
(3,(1,'two',3),3.25)
如果你知道一个序列(元组字符串)的长度,那么可以使用Python中的多重赋值语句方便地提取单个元素。
x, y = (3, 4)#x会被绑定到3,y会被绑定到4。
a, b, c = 'xyz'#会将a绑定到x、b绑定到y、c绑定到z。
元组和字符串一样,范围也是不可变的。
range函数会返回一个range类型的对象,最常用在for循环中。range函数接受3个整数参数:start、stop和step。
#除了连接操作和重复操作,其他所有能够在元组上进行的操作同样适用于范围。
range(10)[2:6][2]
4
range(0,7,2)==range(0,8,2)#值就是True
range(0,7,2)==range(6,-1,-2)#注意:值是False。因为尽管这两个范围包含同样的
列表
:与元组类似,也是值的有序序列,每个值都可以由索引进行标识。
t1=[]#空list
t2=[1]#单元素list
L = ['I did it all', 4, 'love']
for i in range(len(L)):
print(L[i])
I did it all
4
love
#list切片操作
[1, 2, 3, 4][1:3][1]
3
列表与元组相比有一个特别重要的区别:列表是可变的,而元组和字符串是不可变的。
#解释器会创建两个新列表,然后为其绑定合适的变量
Techs = ['MIT', 'Caltech']
Ivys = ['Harvard', 'Yale', 'Brown']
#也会创建新的列表并为其绑定变量。这些列表中的元素也是列表。
Univs = [Techs, Ivys]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
print('Univs =', Univs)
print('Univs1 =', Univs1)
print(Univs == Univs1)
Univs = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
True
看上去好像Univs和Univs1被绑定到同一个值,但这个表象具有欺骗性。Univs和Univs1被绑定到不同的对象,可以使用Python内置函数id验证这一点,id会返回一 个对象的唯一整数标识符。可以用这个函数检测对象是否相等。
print(Univs == Univs1) #测试值是否相等
print(id(Univs) == id(Univs1)) #测试对象是否相等
print('Id of Univs =', id(Univs))
print('Id of Univs1 =', id(Univs1))
True
False
Id of Univs = 81469128
Id of Univs1 = 80584584
Univs中的元素不是Techs和Ivys绑定的列表的复制,而是这些列表本身。 Univs1中的元素也是列表,与Univs中的列表包含同样元素,但不同于Univs中的那些列表。
print('Ids of Univs[0] and Univs[1]', id(Univs[0]), id(Univs[1]))
print('Ids of Univs1[0] and Univs1[1]', id(Univs1[0]), id(Univs1[1]))
Ids of Univs[0] and Univs[1] 81469320 80977096
Ids of Univs1[0] and Univs1[1] 81465928 81555848
为什么这一点很重要呢?因为列表是可变的。append方法具有副作用。它不创建一个新列表,而是通过向列表Techs的末尾添加一个新元素——字符串'RPI'
#注意Techs这个对象改变了,同时改变的只有Univs,因为Univs = [Techs, Ivys]
Techs.append('RPI')
print('Univs =', Univs)
print('Univs1 =', Univs1)
Univs = [['MIT', 'Caltech', 'RPI', 'RPI'], ['Harvard', 'Yale', 'Brown']]
Univs1 = [['MIT', 'Caltech'], ['Harvard', 'Yale', 'Brown']]
这种情况称为对象的别名,即两种不同的方式可以引用同一个列表对象。一种方式是通过变量Techs,另一种方式是通过Univs绑定的list对象中的第一个元素。我们可以通过任意一种方式改变这个对象,而且改变的结果对两种方式都是可见的。这非常方便,但也留下了隐患。无意形成的别名会导致程序错误,而且这种错误非常难以捕获。
#和元组一样,可以使用for语句遍历列表中的元素。
for e in Univs:
print('Univs contains', e)
print(' which contains')
for u in e:
print(' ', u)
Univs contains ['MIT', 'Caltech', 'RPI', 'RPI']
which contains
MIT
Caltech
RPI
RPI
Univs contains ['Harvard', 'Yale', 'Brown']
which contains
Harvard
Yale
Brown
我们将一个列表追加到另一个列表中时,如Techs.append(Ivys),会保持原来的结构。也就是说,结果是一个包含列表的列表。如果我们不想保持原来的结构,而想将一个列表中的元素添加到另一个列表,那么可以使用列表连接操作或extend方法。如下所示:
#操作符+确实没有副作用,它会创建并返回一个新的列表。相反,extend和append都会改变L1。
L1 = [1,2,3]
L2 = [4,5,6]
L3 = L1 + L2
print('L3 =', L3)
L1.extend(L2)
print('L1 =', L1)
L1.append(L2)
print('L1 =', L1)
L3 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6]
L1 = [1, 2, 3, 4, 5, 6, [4, 5, 6]]
下面给出了一些列表操作。请注意,除了count和index外,这些方法都会改变列表。
克隆
:使用切片操作复制某个列表。 我们通常应该尽量避免修改一个正在进行遍历的列表。例如,考虑以下代码:
def removeDups(L1, L2):
"""假设L1和L2是列表,
删除L2中出现的L1中的元素"""
for e1 in L1:
if e1 in L2:
L1.remove(e1)
L1 = [1,2,3,4]
L2 = [1,2,5,6]
removeDups(L1, L2)
print('L1 =', L1)
L1 = [2, 3, 4]
在for循环中,Python使用一个内置计数器跟踪程序在列表中的位置,内部计数器在每次迭代结束时都会增加1。当计数器的值等于列表的当前长度时,循环终止。如果循环过程中列表没有发生改变,那么这种机制是有效的,但如果列表发生改变,就会产生出乎意料的结果。本例中,内置计数器从0开始计数,程序发现了L1[0]在L2中,于是删除了它——将L1的长度减少到3。然后计数器增加1,代码继续检查L1[1]的值是否在L2中。请注意,这时已经不是初始的L1[1]的值(2)了,而是当前的L1[1]的值(3)。
#解决方案:避免这种问题的方法是使用切片操作克隆(即复制)这个列表,并使用for e1 in L1[:]这种写法。
def removeDups(L1, L2):
"""假设L1和L2是列表,
删除L2中出现的L1中的元素"""
for e1 in L1[:]:
if e1 in L2:
L1.remove(e1)
L1 = [1,2,3,4]
L2 = [1,2,5,6]
removeDups(L1, L2)
print('L1 =', L1)
L1 = [3, 4]
在Python中,切片不是克隆列表的唯一方法。
列表推导式提供了一种简洁的方式,将某种操作应用到序列中的一个值上。它会创建一个新 列表,其中的每个元素都是一个序列中的值(如另一个列表中的元素)应用给定操作后的结果
mixed = [1, 2, 'a', 3, 4.0]
print([x**2 for x in mixed if type(x) == int])
[1, 4, 9]
在Python中,函数是一等对象。这意味着我们可以像对待其他类型的对象(如int或list)一样对待函数。
# 使用函数作为实参可以实现一种名为高阶编程的编码方式,这种方式与列表结合使用非常方便
#将函数应用到列表中的元素
def applyToEach(L, f):
"""假设L是列表,f是函数
将f(e)应用到L的每个元素,并用返回值替换原来的元素"""
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.33]
print('L =', L)
print('Apply abs to each element of L.')
applyToEach(L, abs)
print('L =', L)
print('Apply int to each element of', L)
applyToEach(L, int)
print('L =', L)
L = [1, -2, 3.33]
Apply abs to each element of L.
L = [1, 2, 3.33]
Apply int to each element of [1, 2, 3.33]
L = [1, 2, 3]
Python中有一个内置的高阶函数map,它的功能与applyToEach函数相似,但适用范围更广。 1.map函数被设计为与for循环结合使用。在map函数的最简形式中,第一个参数是个一元函数(即只有一个参数的函数),第二个参数是有序的值集合,集合中的值可以一元函数的参数。 2.在for循环中使用map函数时,它的作用类似于range函数,为循环的每次迭代返回一个值。这些值是对第二个参数中的每个元素应用一元函数生成的。例如,下面的代码:
L1 = [1, 28, 36]
L2 = [2, 57, 9]
for i in map(min, L1, L2):
print(i)
1
28
Python还支持创建匿名函数(即没有绑定名称的函数),这时要使用保留字lambda。Lambda表达式的例子:
L = []
for i in map(lambda x, y: x**y, [1 ,2 ,3, 4], [3, 2, 1, 0]):
L.append(i)
print(L)
[1, 4, 3, 1]
str、tuple、range和list。它们的共同之处在于,都可以使用下面描述的操作:
因为字符串只能包含字符,所以应用范围远远小于元组和列表。但另一方面,处理字符串时有大量内置方法可以使用,这使得完成任务非常轻松。请记住,字符串是不可变的,所以这些方法都返回一个值,而不会对原字符串产生副作用。
字典
:(dict,dictionary的缩写)字典类型的对象与列表很相似,区别在于字典使用键对其中的值进行引用,可以将字典看作一个键/值对的集合。字典类型的字面量用大括号表示,其中的元素写法是键加冒号再加上值。
monthNumbers = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5,1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May'}
print('The third month is ' + monthNumbers[3])
dist = monthNumbers['Apr'] - monthNumbers['Jan']
print('Apr and Jan are', dist, 'months apart')
The third month is Mar
Apr and Jan are 3 months apart
dict中的项目是无序的,不能通过索引引用。这就是为什么monthNumbers[1]确定无疑地指向键为1的项目,而不是第二个项目。
#字典中增加或改变项目
monthNumbers['June'] = 6
monthNumbers['May'] = 'V'
#不同语言互译
EtoF = {'bread':'pain', 'wine':'vin', 'with':'avec', 'I':'Je','eat':'mange', 'drink':'bois', 'John':'Jean','friends':'amis', 'and': 'et', 'of':'du','red':'rouge'}
FtoE = {'pain':'bread', 'vin':'wine', 'avec':'with', 'Je':'I','mange':'eat', 'bois':'drink', 'Jean':'John','amis':'friends', 'et':'and', 'du':'of', 'rouge':'red'}
dicts = {'English to French':EtoF, 'French to English':FtoE} #俩字典合二为一
def translateWord(word, dictionary):
if word in dictionary.keys():
return dictionary[word]
elif word != '':
return '"' + word + '"'
return word
def translate(phrase, dicts, direction):
UCLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
LCLetters = 'abcdefghijklmnopqrstuvwxyz'
letters = UCLetters + LCLetters
dictionary = dicts[direction]
translation = ''
word = ''
for c in phrase:
if c in letters:
word = word + c
else:
translation = translation+ translateWord(word, dictionary) + c
word = ''
return translation + ' ' + translateWord(word, dictionary)
print (translate('I drink good red wine, and eat bread.',dicts,'English to French'))
print (translate('Je bois du vin rouge.',dicts, 'French to English'))
Je bois "good" rouge vin, et mange pain.
I drink of wine red.
多数编程语言都不包含这种提供从键到值的映射关系的内置类型。然而,程序员可以使用其他类型实现同样的功能。例如,使用其中元素为键/值对的列表就可以轻松实现字典,然后可以编写一个简单的函数进行关联搜索,如下所示:
#这种实现的问题在于计算效率太低。最坏情况下,程序执行一次搜索可能需要检查列表中的每一个元素
def keySearch(L, k):
for elem in L:
if elem[0] == k:
return elem[1]
return None
可以使用for语句遍历字典中的项目。但分配给迭代变量的值是字典键,不是键/值对。迭代过程中没有定义键的顺序。
monthNumbers = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5,
1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May'}
keys = []
for e in monthNumbers:
keys.append(str(e))
print(keys)
keys.sort()
print(keys)
['3', 'May', '2', 'Feb', '1', '5', 'Jan', 'Mar', '4', 'Apr']
['1', '2', '3', '4', '5', 'Apr', 'Feb', 'Jan', 'Mar', 'May']
keys方法返回一个dict_keys类型的对象。①这是view对象的一个例子。视图中没有定义视图的顺序。视图对象是动态的,因为如果与其相关的对象发生变化,我们就可以通过视图对象察觉到这种变化。例如:
birthStones = {'Jan':'Garnet', 'Feb':'Amethyst', 'Mar':'Acquamarine','Apr':'Diamond', 'May':'Emerald'}
months = birthStones.keys()
print(months)
birthStones['June'] = 'Pearl'
print(months)
dict_keys(['May', 'Feb', 'Apr', 'Jan', 'Mar'])
dict_keys(['May', 'Feb', 'Jan', 'Mar', 'Apr', 'June'])
可以使用for语句遍历dicttype类型的对象,也可以使用in检测其中的成员。dicttype类型的对象可以很容易地转换为列表,如list(months)。
字典方法: