Python核心编程--学习笔记--7--字典和集合

时间:2023-03-09 03:14:04
Python核心编程--学习笔记--7--字典和集合

  本章介绍Python语言中的映射类型(字典)和集合类型,包括操作符、工厂函数、内建函数与方法。

1 字典

  字典是Python中唯一的映射类型——键key直接映射到值value。字典是容器类型,其对象是可变的。字典中的数据是无序排列的。是哈希表。

  创建字典——直接赋值{}、工厂函数dict()、内建方法fromkeys():

>>> dict1={}
>>> dict2={'name':'earth','port':80}
>>> dict1, dict2
({}, {'name': 'earth', 'port': 80}) >>> dict3=dict(['ab','cd'])
>>> dict3
{'a': 'b', 'c': 'd'} >>> dict4={}.fromkeys(['ab','cd']) #对应同一值,默认对应None
>>> dict5={}.fromkeys(('ab','cd'), -1)
>>> dict4,dict5
({'ab': None, 'cd': None}, {'ab': -1, 'cd': -1})

  访问字典元素——dict[key]、dict.method():

>>> d={'a':1, 'b':2, 'c':3}
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> d['b'] #直接通过dict[key]访问值
2
>>> d['d'] #如果key不存在则报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'd'
>>> 'a' in d #判断dict中是否有key
True
>>> d.has_key('c') #用方法来判断是否有key,不再推荐使用
True >>> d.values(), d.keys(), d.items() #dict方法,分别返回 键、值、键值对 组成的无序列表
([1, 3, 2], ['a', 'c', 'b'], [('a', 1), ('c', 3), ('b', 2)]) >>> for key in d:  #直接用迭代器访问字典,迭代的是字典的键
... print key
...
a
c
b >>> print '%(a)d %(b)d %(c)d' % d #字典简化了print的格式化符
1 2 3

  添加新元素/修改已存在元素——dict[key]=new_value:

>>> d['d'] = 4    #不存在的键,则添加键值对
>>> d['a'] = -1 #存在的键,则覆盖原来的值
>>> d
{'a': -1, 'c': 3, 'b': 2, 'd': 4}

  删除字典元素和字典:

 del dict[key]   #删除键为“name”的条目
dict.clear() #删除dict中所有的条目,成为空字典
del dict #删除整个dict字典
dict.pop(key) #删除并返回键为key的条目

2 字典操作符

2.1 标准类型操作符

  比较运算符、逻辑运算符 同样适用于字典。

  比较字典大小不常用,算法流程为:①比较长度,长度大的字典大。②长度一样时,比较键,键比较顺序和keys()方法返回的顺序一样。(相同的键会映射到哈希表的同一位置,保证了这一过程的一致性) ③键一样时,按照键的顺序比较相应的值。④之前的比较都一样,则两个字典相同。

2.2 字典操作符

  键查找操作符[]、键成员操作符[not] in。

3 字典相关的内建函数和工厂函数

3.1 标准类型函数

  type()、str()、cmp()、len() 同样适用于字典。cmp()的比较过程见2.1。

3.2 字典相关的函数

  dict()——参数可以是 空/可迭代的/另一个字典:

>>> dict()  #生成空字典
{}
>>> dict( zip(('x', 'y'), (1, 2)) ) #参数为一个列表,即一个可迭代对象,其中每个可迭代的元素必须成对出现
{'y': 2, 'x': 1}
>>> dict({'x':1}) #参数为另一个字典,即浅拷贝,效率不高,不要使用这种方法
{'x': 1}
>>> {'x':1}.copy() #高效率的浅拷贝
{'x': 1}
>>> dict(ab=1,cd=2) #关键字参数
{'ab': 1, 'cd': 2}

  hash(obj)——返回对象的哈希值,可用于判断是否能作为字典的键。如果不可哈希则产生TypeError错误。

4 字典的内建方法

 dict.clear()   #删除字典中所有元素
dict.copy() #返回字典(浅拷贝)的一个副本
dict.fromkeys(seq, val=None) #创建并返回一个新字典,以seq中的元素为键,val为所有键对应的初始值(默认为None)
dict.get(key, default=None) #读取key对应的值,如果不存在此键,则返回default的值(默认值为None)。
dict.has_key(key) #如果键存在,返回True。推荐使用成员操作符。
dict.items() #返回一个包含字典中(键, 值)对元组的列表
dict.iter*() #方法iteritems(),iterkeys(),itervalues(),返回一个迭代子而非列表,当元素很多时可以节省内存。
dict.keys() #返回一个包含字典中键的列表
dict.pop(key[, default]) #如果键存在,删除键值对并返回值;如果key键不存在,且没有指定default,则引发KeyError异常
dict.popitem() #删除并返回第一个键值对,如果字典为空则产生错误。
dict.setdefault(key, default=None) #如果不存在key键,由dict[key]=default为它赋值并返回值;存在则直接返回值
dict.update(dict2) #将字典dict2的键-值对添加到字典dict,重复的键则覆盖dict中原值
dict.values() #返回一个包含字典中所有值的列表

5 字典的键

  一个键只能对应一个值。

  键必须是可哈希的,不可变类型——字符串、数字、只包含不可变类型的元组——都是可哈希的。值相同的数字哈希值也相同hash(1) == hash(1.0)。

  实现了__hash__()特殊方法的类,如果该方法返回一个不可变类型,也是可哈希的。因为解释器调用哈希函数,根据字典中键的值来计算数据存储的位置。

6 集合类型

  集合对象是一组无序列的可哈希的值,集合成员可以用作字典的键。分为可变集合和不可变集合,可变集合不可哈希。

  集合:支持[not] in检查成员、len()得到元素个数、for迭代集合成员。不支持索引、切片,没有键的概念。

  Python中的集合和数学上的集合差不多,操作符如下:

 数学符号  Python符号    说明
  ∈   in      是集合成员
  ∉   not in   不是集合成员
  =   ==       等于
  ≠   !=       不等于
  ⊂   <       真子集
  ⊆   <=       子集
  ⊃   >       严格超集
  ⊇   >=       超集
  ∩   &        交集
  ∪   |        并集
  -   -        差补
  △   ^       对称差(A^B = A|B - A&B)

  创建结合类型——只能用工厂函数set()和frozenset():

>>> s=set('abc')            #可变集合,传入一个序列或者可迭代的对象。
>>> fs=frozenset([1,2,3]) #不可变集合
>>> s
set(['a', 'c', 'b'])
>>> fs
frozenset([1, 2, 3])

  访问集合成员——检查成员、遍历成员:

>>> 'c' in s       #检查成员
True
>>> for i in fs: #遍历成员
... print i
...
1
2
3

  更新集合——使用集合内建的方法和操作符,只能对可变集合,进行添加或删除元素(具体见第8节的内建函数):

>>> s.add(321)     #可变集合添加元素
>>> s
set(['a', 321, 'c', 'b'])
>>> fs.remove(1) #不可变集合删除元素,错误
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'remove'

  删除集合——del set即可。

7 集合类型操作符

7.1 标准类型操作符

  成员操作符—— in、not in

  集合等价—— ==、!=(两个集合的成员完全相同时,集合相同,不区分可变与不可变类型)

  子集/超集—— 真子集<、子集<=、真超集>、超集>=

7.2 集合类型操作符

  适用于所有集合——并集|、交集&、差补-、对称差^,返回一个新集合,参与运算的集合不变:

>>> s1=set([1,2])
>>> s2=set([2,3])
>>> fs=frozenset([2,3])
>>> s1, s2, fs
(set([1, 2]), set([2, 3]), frozenset([2, 3]))
>>> s1|s2, s1.union(s2) #s1和s2的交集,分别使用符号和方法
(set([1, 2, 3]), set([1, 2, 3]))
>>> s1&s2, s1.intersection(s2) #交集
(set([2]), set([2]))
>>> s1-s2, s1.difference(s2) #差补,即属于s1但是不属于s2的元素集合
(set([1]), set([1]))
>>> s1^s2, s1.symmetric_difference(s2) #对称差,即s1|s2 - s1&s2
(set([1, 3]), set([1, 3]))
>>> s1|fs, fs|s1 #可变与不可变类型集合运算,返回类型与左边的类型相同
(set([1, 2, 3]), frozenset([1, 2, 3]))

  仅适用于可变集合——即集合运算的复合运算—— |=、&=、-=、^=,直接应用于左边的集合,没有返回值:

>>> s1 = set([1,2])
>>> s2 = set([1,2])
>>> s3 = set([2,3]) ###原始集合。以下运算都使用这三个数据,由于是在原集合上直接修改,每次运算都重新赋值,以下省略而已。 >>> s1 |= s3 #求并集并将新集合赋值给左值,没有返回值。分别使用 符号和方法。
>>> s2.update(s3)
>>> s1,s2
(set([1, 2, 3]), set([1, 2, 3])) >>> s1 &= s3 #交集
>>> s2.intersection_update(s3)
>>> s1,s2
(set([2]), set([2])) >>> s1 -= s3 #差补
>>> s2.difference_update(s3)
>>> s1,s2
(set([1]), set([1])) >>> s1 ^= s3 #对称差
>>> s2.symmetric_difference_update(s3)
>>> s1,s2
(set([1, 3]), set([1, 3]))

8 内建函数

8.1 标准类型函数

  len()——返回集合成员个数。

8.2 工厂函数

  set()、frozenset()——生成一个集合。参数必须是可迭代的——序列、迭代器、支持迭代的对象(文件或字典)。

9 集合的内建方法

  适用于所有集合的,之前也讲述过:

 s.issubset(t)             #符号<= 如果s是t的子集,则返回True
s.issuperset(t) #符号>= 如果t是s的超集,则返回True
s.union(t) #符号| 返回一个新集合,该集合是s和t的并集
s.intersection(t) #符号& 返回一个新集合,该集合是s和t的交集
s.isdisjoint(t) #如果s和t没有交集,则返回True
s.difference(t) #符号- 返回一个新集合,该集合成员是s的成员,但不是t的成员
s.symmetric_difference(t) #符号^ 返回一个新集合,该集合是s或t的成员,但不是s和t共有的成员
s.copy() #返回一个新集合,它是集合s的浅拷贝。比工厂函数快

  仅适用于可变集合,有之前讲述的,也有一些新方法:

 s.update(t)                      #符号|= s为s和t的并集
s.intersection_update(t) #符号&= s为s和t的交集
s.difference_update(t) #符号-= s中的成员是属于s但不包含在t中的元素
s.symmetric_difference_update(t) #符号^= s中的成员是属于s或t,但不属于s和t共有
s.add(obj) #在集合s中添加对象obj
s.remove(obj) #从集合s中删除对象obj,如果obj不存在,则引发KeyError错误
s.discard(obj) #从集合s中删除对象obj,如果obj不存在也不报错
s.pop() #删除集合s中的任意一个对象,并返回它
s.clear() #删除集合s中的所有元素 

  操作符和方法:当用操作符时,操作符两边的操作数必须是集合。在使用内建方法时,对象也可以是迭代类型的。

练习题

7-8 人力资源。创建一个简单的雇员姓名和编号的程序,让用户输入一组雇员姓名和编号,你的程序可以提供按照姓名排序输出的功能,雇员姓名显示在前面,后面是对应的雇员编号。附加题:添加一项功能,按照雇员编号的顺序输出数据。

 #!/usr/bin/env Python

 '''sorted by key or value'''

 idx = 0
emp = {} def mycmp(a, b):
if a[idx] > b[idx]:
return 1
elif a[idx] < b[idx]:
return -1
else:
return 0 while True:
info = raw_input('Enter like this: name:id ,q to quit: ')
if info == 'q':
break
else:
info = info.split(':')
emp[info[0]] = int(info[1]) print sorted(emp.items(), mycmp)
idx = 1
print sorted(emp.items(), mycmp)

7-8

7–13 随机数。使用random模块中的randint()或randrange()方法生成一个随机数集合:从0到9(包括9)中随机选择,生成1到10个随机数。这些数字组成集合A(A可以是可变集合,也可以不是)。同理,按此方法生成集合B。每次新生成集合A和B后,显示结果A|B和A&B。

 #!/usr/bin/env Python

 import random

 def make_list():
new_list = []
for i in range(random.randint(1, 10)):
new_list.append(random.randint(0, 9))
return set(new_list) A = make_list()
B = make_list()
print A, B print A|B
print A&B

7-13