字典:当索引不好用时
字典是一种通过名字引用值的数据结构。这种结构类型称为映射。字典是Python中唯一內建的映射类型。
4.1 字典的使用
现实中的字典以及在Python中的字典都进行了构造,从而可以轻松查到某个特定的词语(键),从而找到它的定义(值)
某些情况,字典比列表更适用:
[if !supportLists]① [endif]征游戏棋盘的状态,每个键都是由坐标值组成的元组
[if !supportLists]② [endif]存储文件修改次数,用文件名作为键;
[if !supportLists]③ [endif]数字电话/地址簿
教训就是:电话号码(以及其它可能以0开头的数字)应该表示为数字字符串,而不是整数。
4.2 创建和使用字典
【代码】
items = {'Alice':'2341','Beth':'9102','Ceil':'3258'}
print(items)
#字典由多个键值对组成。每个键和它的值之间用冒号(:)分隔,项之间用逗号(,)隔开,这个字典是由一对大括号括起来。键唯一,而值不唯一
【结果】
{'Beth': '9102', 'Alice': '2341', 'Ceil': '3258'}
4.2.1 dict函数
【代码】
#可以使用dict函数,通过其他映射(比如其他字典)或者(键,值)这样的序列对建立字典items = [('name','Gumby'),('age','42')] #键值对被元素括起来d = dict(items)
print(d)
print(d['name'])
#dict函数也可以通过关键字参数来创建字典d = dict(name = 'Gumby',age = 42) #通过向函数传入参数print(d)
#还能以映射作为dict函数的参数,以建立其项与映射相同的字典()
【结果】
{'name': 'Gumby', 'age': '42'}
Gumby
{'name': 'Gumby', 'age': 42}
4.2.2 基本字典操作
字典的基本行为在很多方面与序列(sequence)类似
len(d) 返回d中项(键-值对)的数量
d[k] 返回关联到键k上的值
d[k] = v 将值v关联到键k上
del d[k] 删除键为k的项
k in d 检查d中是否含有键为k的项
字典和列表的区别:
键类型:字典的键不一定是整型数据,比如可能是浮点型、字符串或者元组
自动添加:即使那个键起初在字典中不存在,也可以给它分配一个值,这样字典就会建立新的项。而(在不使用append或者其他类似操作情况下)不能将值关联到列表范围之外的索引上。
成员资格:表达式 k in d (d为字典)查找的是键,而不是值。表达式v in l (l为列表)则用来查找值,而不是索引。
注:在字典中检查键的成员资格比在列表中检查值的成员资格效率高,数据结构规模越大,效果越明显。
【代码】
#键可以为任何不可变类型--是字典最强大的地方。x = {}
x[
42] = 'Foobar'
print(x)
x = []
x[
42] = 'Foobar'
【结果】
{42: 'Foobar'}
Traceback (most recent call last):
File"D:/python_file/20180113/test.py", line 7, in
x[42] = 'Foobar'
IndexError: list assignment index out of range
字典示例:
【代码】
#简单数据库
#
使用人名作为键的字典,每个人用另一个字典来表示,其键'phone'和'addr'分别表示他们的电话号码和地址people = {
'Alice':{
'phone':'2341',
'addr':'Foo drive 23'
},
'Beth':{
'phone':'9102',
'addr':'Bar street 42'
},
'Cecil':{
'phone':'3158',
'addr':'Baz avenue 90'
}
}
#针对电话号码和地址使用的描述性标签,会在打印输出时用到labels = {
'phone':'phone_number',
'addr':'address'
}
name =
input('please
input name:')
#查找电话号码还是地址?使用正确的键request = input('please give me a
request:')
#使用正确的键if request == 'p': key
= 'phone'
if request == 'a': key
= 'addr'
#如果名字是字典中的有效键才打印信息:if name in people:
print("%s' s %s is %s." % (name,labels[key],people[name][key]))
【结果】
please input name:Beth
please give me a request:p
Beth' s phone_number is 9102.
4.2.3 字典的格式化字符串
【代码】
#第三章中已经使用了字符串格式化功能来格式化元组中的所有值。如果使用字典而不是元组来做这个工作,会使字符串格式化更酷一些。
#
在每个转换说明符中的%后面,可以加上(用圆括号括起来的)键,后面再跟上其他说明元素。phonebook = {'Beth':'9102','Alice':'2341','Ceil':'3258'}
print("Ceil's
phone number is %(Ceil)s." % phonebook)
【结果】
Ceil's phone number is 3258.
【代码】
template = '''
%(title)s
%(title)s
%(text)s
'''
data = {'title':'My Home Page','text':'Welcome
to my home page!'}
print(template% data)#也就是说把 %(title)s 放在了字符串的内部,到外部的时候,就不需要写这些东西了--》直接template % data
【结果】
My Home Page
My HomePage
Welcome to myhome page!
4.2.4 字典方法
就像其他內建类型一样,字典也有方法。这些方法非常有用,但是可能不会像使用列表或者字符串方法那样频繁使用。可以了解一下哪些方法可用,然后需要的时候再查看特定方法的具体用法。
[if !supportLists]1. [endif]clear
【代码】
#clear方法清除字典中所有的项。这是个原地操作,所有无返回值(或者说返回None)d = {}
d[
'name'] = 'Gumby'
d['age'] = '42'
print(d)
returned_value = d.clear()
print(d)
print(returned_value)
【结果】
{'age': '42', 'name': 'Gumby'}
{}
None
【代码】
#为什么这个方法有用呢?x = {}
y = x
x[
'key'] = 'value'
print(y)
x = {}
print(y,'\n')
#然后是第二种情况:x = {}
y = x
x[
'key'] = 'value'
print(y)
x.clear()
print(y)
#可知,两种情况中,x和y最初对应同一个字典。情况1中,通过将x关联到一个新的空字典来“清空”它,这对y没有影响。但如果真的想清空原始字典中所有元素,必须使用clear方法。可见,变量名相当于指针,指向的内容相当于数据,而clear()函数正是对数据进行操作的。
【结果】
{'key': 'value'}
{'key': 'value'}
{'key': 'value'}
{}
[if !supportLists]2. [endif]copy
【代码】
#copy方法返回一个具有相同键-值对的新字典(这个方法实现的是浅复制(shallow copy),因为值本身就是相同的,而不是副本)x = {'username':'admin','machines':['foo','bar','baz']}
y = x.copy()
y[
'username'] = 'mlh'
#python2 中删除操作使用y['machines'].remove('bar')
#
在python3 中删除操作如下:del y['machines'][1]
print(y)
print(x)
#可以看到,在副本中替换值的时候,原始字典不受影响,但是,如果修改了(删除)某个值,原始字典也会改变
#
即(副替换主不替换,副修(删除,新增)则主修)
【结果】
{'username': 'mlh', 'machines': ['foo', 'baz']}
{'username': 'admin', 'machines': ['foo', 'baz']}
【代码】
# 但是,如果修改了某个值(删除或新增,而不是替换),原始的字典也会改变,因为同样的值存储在原字典中
#
避免这种现象的方法就是使用深复制(deep copy),复制其包含的所有值。可以使用copy模块的deepcopy来完成from copy import deepcopy
d = {}
d[
'names'] = ['Alfred','Bertrand']
c = d.copy()
dc = deepcopy(d)
d[
'names'].append(['Clive'])
print(c)
print(dc) #可以看出,新增操作对于深复制操作得到的字典没有影响
【结果】
{'names': ['Alfred', 'Bertrand', ['Clive']]}
{'names': ['Alfred', 'Bertrand']}
[if !supportLists]3. [endif]fromkeys
【代码】
#fromkeys方法使用给定的键建立新的字典,每个键默认对应的值为Noneprint({}.fromkeys(['name','age']))
#首先构造了一个空字典,然后调用它的fromkeys方法,建立另外一个字典。
#
上面的例子有些多余,也可以在所有字典的类型dict上面调用方法print(dict.fromkeys(['name','age']))
【结果】
{'age': None, 'name': None}
{'age': None, 'name': None}
[if !supportLists]4. [endif]get
【代码】
#get方法是个更宽松的访问字典项的方法。一般来说,如果试图访问字典中不存在的项时会出错,而用get不会d = {}
print(d.get('name'))
# print(d['name'])
#还可以自己定义“默认”值,替换Noneprint(d.get('name','N/A'))
#如果键存在,get用起来就像普通的字典查询一样d['name'] = 'Eric'
print(d.get('name'))
【结果】
None
N/A
Eric
【代码】
#使用get()的简单数据库
#
这里添加代码清单4-1中插入数据库的代码people = {
'Alice':{
'phone':'2341',
'addr':'Foo drive 23'
},
'Beth':{
'phone':'9102',
'addr':'Bar street 42'
},
'Cecil':{
'phone':'3158',
'addr':'Baz avenue 90'
}
}
labels = {
#用于显示
'phone':'phone_number',
'addr':'address'
}
name =
input("please
input a name:")
request =
input("please
input a request(p or a):") #查找电话号码还是地址
#
使用正确的键key = request #如果请求既不是'p',也不是'a'if request == 'p':
key =
'phone'
if request == 'a':
key =
'addr'
#使用get()提供默认值person =
people.get(name,{}) #得到的person含有号码和地址label =labels.get(key,key)
result = person.get(key,
'not
available')
print("%s's
%s is %s" %(name,label,result))
#可以看出get方法带来的灵活性使得程序在用户输入我们并未准备的值时也能做出合理的反应。
【结果】
please input a name:Alice
please input a request(p or a):d
Alice's d is not available
[if !supportLists]5. [endif]has_key
【代码】
#has_key方法可以检查字典中是否含有给出的键。表达式 d.has_key(k) 相当于 k in d(python3中不包括这个函数)d = {}
print('name' in d)
d[
'name'] = 'Eric'
print('name' in d)
【结果】
False
True
[if !supportLists]6. [endif]items和iteritems
【代码】
#items方法将所有字典项以列表方式返回,这些列表项中每一项都来自于(键,值)。但项在返回时没有特殊的顺序d = {'title':'Python
Web Site','url':'http://www.python.org','spam':0}
#在python3中,items()和viewitems()一致。print(d.items())
#iteritems方法的作用大致相同,但是会返回一个迭代器对象而不是列表
#
在python3中,items()替换iteritems()可以用于for来循环遍历it = d.items()
print(it)
print(list(it))
#在很多情况下,使用iteeritems更高效(尤其是想要迭代结果的时候)
【结果】
dict_items([('spam', 0), ('url', 'http://www.python.org'), ('title','Python Web Site')])
dict_items([('spam', 0), ('url', 'http://www.python.org'), ('title','Python Web Site')])
[('spam', 0), ('url', 'http://www.python.org'), ('title', 'Python WebSite')]
[if !supportLists]7. [endif]keys和iterkeys
keys方法将字典中的键以列表形式返回,而iterkeys则返回针对键的迭代器
[if !supportLists]8. [endif]pop
【代码】
#pop方法用来获得对应于给定键的值,然后将这个键-值对从字典中移除d = {'x':1,'y':2}
d.pop(
'x')
print(d)
【结果】
{'y': 2}
[if !supportLists]9. [endif]popitem
【代码】
#popitem方法类似于list.pop,后者会弹出列表的最后一个元素。但不同的是,popitem弹出随机的项,因为字典并没有“最后的元素”或者其他有关顺序的概念d = {'url':'http://www.python.org','spam':0,'title':'python
web site'}
print(d)
print(d.popitem())
print(d)
#尽管popitem和列表的pop方法很类似,但字典中没有与append等价的方法。因为字典无序。
【结果】
{'url': 'http://www.python.org', 'spam': 0, 'title': 'python web site'}
('url', 'http://www.python.org')
{'spam': 0, 'title': 'python web site'}
[if !supportLists]10.[endif]setdefault
【代码】
#setdefault方法在某种程度上类似于get方法,就是能够获得与给定键相关联的值,除此之外,setdefault还能在字典中不含有给定键的情况下设定相应的键值d = {}
d.setdefault(
'name','N/A')
print(d)
d[
'name'] = 'Gumby'
d.setdefault('name','N/A')
print(d)
【结果】
{'name': 'N/A'}
{'name': 'Gumby'}
[if !supportLists]11.[endif]update
【代码】
#update方法可以利用一个字典项更新另一个字典d = {
'title':'Python Web Site',
'url':'http://www.python.org',
'changed':'Mar 14 22:09:15 MET2008'
}
x = {
'title':'Python
Language Website'}
d.update(x)
print(d)
#update方法可以使用与调用dict函数同样的方式进行调用,这就意味着update可以和映射、拥有(键、值)对的队列(或者其他可迭代对象)以及关键字参数一起调用
【结果】
{'title': 'Python Language Website', 'url': 'http://www.python.org','changed': 'Mar 14 22:09:15 MET 2008'}
[if !supportLists]12.[endif]values和itervalues
【代码】
#values方法以列表的形式返回字典中的值(itervalues返回值的迭代器)。与返回键的列表不同的是,返回值的列表中可以包含重复的元素d = {}
d[
1] = 1
d[2] = 2
d[3] = 3
d[4] = 4
print(d.values())
【结果】
dict_values([1, 2, 3, 4])
映射:映射可以使用任何不可变对象表示元素。最常用的类型是字符串和元组。Python內建的映射类型是字典。
利用字典格式化参数:可以通过在格式化说明符中包括名称(键)来对字典应用字符串格式化操作。当在字符格式化中使用元组时,还需要对元组中每一个元素都设定“格式化说明符”。
字典的方法:字典有很多方法,调用的方式和调用列表以及字符串方法的方式相同。
第五章 条件、循环和其他语句
下面将深入学习条件语句和循环语句。还有几种基本语句更多的使用方法。随后会学习列表推导式(list comprehension)如何扮演循环和条件语句的角色—尽管它本身是表达式。最后学习pass、del和exec语法的用法。
5.1 print和import的更多信息
对于很多应用程序来说,使用logging模块记日志比print语句更合适。
5.1.1 使用逗号输出
【代码】
print((1,2,3)) #会打印元组print(1,2,3) #不会打印元组print('hello'+',','world') #如果希望在hello后紧接着打印逗号,可以使用+的方式
【结果】
(1, 2, 3)
1 2 3
hello, world
5.1.2 把某件事作为另一件事导入
import somemodule
from somemodule import somefunction
from somemodule import somefunction,anotherfunction,yetanotherfunction
from somemodule import *
只有确定自己想要从给定的模块导入所有功能时,才应该使用最后一个版本。但如果两个模块都有open函数,只需使用第一种方式导入,然后向下面这样使用函数:
module1.open(…)
module2.open(…)
但还有另外的选择:可以在语句末尾增加一个as语句,在该语句后给出名字,或为整个模块提供别名:
import math as foobar
foobar.sqrt(4)
也可以为函数提供别名:
from math import sqrt as foobar
foobar(4)
对于open函数,可以像下面这样使用:
from module1 import open as open1
from module2 import open as open2
5.2 赋值魔法
就算是不起眼的赋值语句,也有一些特殊的技巧
5.2.1 序列解包
【代码】
#多个赋值操作可以同时进行x,y,z = 1,2,3
print('--1:',x,y,z)
#很有用吧,用它交换两个(或更多个)变量也是没问题的x,y = y,x
print('--2:',x,y,z)
#事实上,这就是序列解包,或可迭代解包----将多个值的序列解开,然后放到变量的序列中。values = 1,2,3
print('--3:',values)
x,y,z = values
print('--4:',x)
#当函数或者方法返回元组,(或者其他序列或可迭代对象)时,这个特性尤其有用。假设需要获取(和删除)字典中任意的项(键-值对),可以使用popitem方法,该方法将项作为元组返回。那么这个元组就可以直接赋值到两个变量中:scoundrel = {'name':'Robin','girlfriend':'Marian'}
key,value = scoundrel.popitem()
print('--5:',key,value)
#注:python3中有另外一个解包的特性:可以像在函数的参数列表中一样使用星号运算符
【结果】
--1: 1 2 3
--2: 2 1 3
--3: (1, 2, 3)
--4: 1
--5: name Robin
5.2.2 链式赋值
【代码】
#链式赋值是将同一个值赋给多个变量的捷径,不过这里只处理一个值def foo():
return 1
x = y = foo()
#和下面两条语句等价y = foo()
x = y
#注意与下面的语句不一定等价x = foo()
y = foo()
【结果】
无
5.2.3 增量赋值
【代码】
x = 2
x += 1
x *= 2
print(x)
#对于其他类型也适用(只要二元运算符本身适用于这些数据结构类型即可)fnord = 'foo'
fnord += 'bar'
fnord *= 2
print(fnord)
#注:增量赋值可以让代码更加紧凑和简练,很多情况下易读
【结果】
6
foobarfoobar
5.3 语句块:缩排的乐趣
#语句块并非一种语句,而是……
#
语句块是在条件为真(条件语句)时执行或者执行多次(循环语句)的一组语句。在代码前放置空格来缩进语句即可创建语句块
#
在python中,冒号(:)用来标识语句块的开始,块中每一个语句都是缩进的(缩进量相同)。当回退到和已经闭合的块一样的缩进量时,就表示当前块已经结束了。
5.4 条件和条件语句
这里会学习让程序是否执行语句块的方法。
5.4.1 布尔变量的作用
下面的值在作为布尔表达式的时候,会被解释器看做假(false)
False None 0 “” () [] {}
也就是说,Python中的所有值都能被解释为真值
【代码】
print(True)
print(False)
print(True == 1)
print(False == 0)
print(True + False + 42)
#布尔值True和False属于布尔类型,bool函数可以用来转换其他值(和list、str以及tuple一样)转换其他值print(bool('I
think,therefore I am'))
print(bool(42))
print(bool(''))
print(bool(0))
#注:尽管[]和""都是假值(也就是说bool([])==bool("")==False),它们本身却不相等(也就是说[]!=""),对于其他价值对象也是如此
【结果】
True
False
True
True
43
True
True
False
False
5.4.2 条件执行和if语句
if子句
5.4.3 else子句
else子句
5.4.4 elif子句
elif子句
5.4.5 嵌套代码块
嵌套代码块
5.4.6 更复杂的条件
1.比较运算符
表达式描述
x == yx等于y
x < yx小于y
x > yx大于y
x >= yx大于等于y
x <= yx小于等于y
x != yx不等于y
x is yx和y是同一个对象
x is not yx和y是不同的对象
x in yx是y容器(例如,序列)的成员
x not in yx不是y容器(例如,序列)的成员
检查一个整型是否比一个字符串小,看起来也是毫无意义的。但奇怪的是,在Python3.0之前的版本中这确实可以的。我们应当对这种比较敬而远之,因为结果完全不可靠。在Python3.0中,比较不兼容类型的对象已经不再可行。
在Python中比较运算符和赋值运算符一样是可以连接的----几个运算符可以连在一起使用,比如: