我是靠谱客的博主 幸福煎饼,这篇文章主要介绍Python中的字典与集合创建和使用字典字典的变种子类化UserDict不可变字典类型集合集合推导集合的操作,现在分享给大家,希望可以做个参考。

字典类似Java中的Map,字典中的键可以是数字、字符串甚至是元组。

它们有一个共同特点就是不可变的,更进一步说,是可散列的数据类型。

可散列的: 在对象的生命周期汇总,它的散列值(hash code)是不变的,而且这个对象需要实现__hash__()__eq__()方法。如果两个可散列对象是相等的,那么它们的散列值一定是一样的。

上面说元组可以作为键是有个前提的,就是元组包含的所有元素都是可散列的情况下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [33]: tl = (1,2,[30,40]) In [34]: hash(tl) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-34-b7b49ff1c689> in <module> ----> 1 hash(tl) TypeError: unhashable type: 'list' In [36]: d = {tl:'1'} --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-36-8be9aeb1426b> in <module> ----> 1 d = {tl:'1'} TypeError: unhashable type: 'list'

一般用户自定义的类型都是可散列的,散列值是它们的id()函数返回的值。

创建和使用字典

复制代码
1
2
3
4
5
6
7
8
>>> people = {'jack':34,'rose':25,'jack':36} >>> people {'rose': 25, 'jack': 36} >>> type(d) dict

上面就是创建字典的方法,注意,字典中键是唯一的,我输入了两个’jack’,结果取的是后面输入的’jack’。

dict函数

也可以使用dict函数,通过其他映射(字典)或者(键,值)对的序列建立字典

复制代码
1
2
3
4
5
6
7
8
>>> items = [('name','jack'),('age',24)] >>> d = dict(items) >>> d {'age': 24, 'name': 'jack'} >>> d['name'] 'jack'

也可以通过关键字参数来创建字典:

复制代码
1
2
3
4
>>> d = dict(name='rose',age=43) >>> d {'age': 43, 'name': 'rose'}

总结一下创建字典的方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [54]: a = dict(one=1,two=2,three=3) In [55]: b = {'one':1,'two':2,'three':3} In [56]: c = dict(zip(['one','two','three'],[1,2,3])) In [57]: d = dict([('two',2),('one',1),('three',3)]) In [58]: e = dict({'three':3,'one':1,'two':2}) In [59]: a == b == c == d == e Out[59]: True

字典推导

字典推导可以从任何以键值对作为元素的可迭代对象中构建出字典。
下面展示了如何把一个装满元组的列表变成两个不同的字典:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
In [66]: DIAL_CODES = [ ...: (86, 'China'), ...: (91, 'India'), ...: (1, 'United States'), ...: (62, 'Indonesia'), ...: (55, 'Brazil'), ...: (92, 'Pakistan'), ...: (880, 'Bangladesh'), ...: (234, 'Nigeria'), ...: (7, 'Russia'), ...: (81, 'Japan'), ...: ] In [67]: country_code = {country : code for code, country in DIAL_CODES} #country 作为键,code作为值 In [69]: country_code Out[69]: {'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62, 'Brazil': 55, 'Pakistan': 92, 'Bangladesh': 880, 'Nigeria': 234, 'Russia': 7, 'Japan': 81} In [70]: {code : country.upper() for country, code in country_code.items() if code < 66} Out[70]: {1: 'UNITED STATES', 62: 'INDONESIA', 55: 'BRAZIL', 7: 'RUSSIA'}

字典的基本操作

字典中的键可以是任意的不可变类型,比如浮点型、字符串或元组。

字典的基本操作很多方面与序列类似:

  • len(d) 返回d中项(键值对)的数量
  • d[k]返回k对应的值
复制代码
1
2
3
4
5
6
7
8
9
>>> d {'age': 43, 'name': 'rose'} >>> d['age'] 43 >>> d['a'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'a'

如果输入不存在的键,会报错。

  • d[k]=v将值v关联到键k上
复制代码
1
2
3
4
5
>>> d['a'] = 1 >>> d {'a': 1, 'age': 43, 'name': 'rose'}

可以关联一个不存在的键,那么就是添加一个新的键值对。如果关联已存在的键,则是覆盖。

  • del d[k]删除键为k的项
  • k in d检查d是否含有键为k的项

字典的格式化字符串

可以在%后面加上键:

复制代码
1
2
3
4
5
6
>>> people {'rose': 25, 'jack': 36} >>> " rose's age is %(rose)s" % people #%(rose)s 在%s中间加入了(rose) " rose's age is 25"

字典方法

  • clear
    用来清除字典中所有的项。这个是原地操作,也就是无返回值。
复制代码
1
2
3
4
5
6
7
8
>>> d = {} >>> d['name']='Jack' >>> d {'name': 'Jack'} >>> d.clear() >>> d {}
  • copy
    返回一个具有相同键值对的新字典,该方法实现的是浅克隆。
复制代码
1
2
3
4
5
6
7
8
9
>>> x = {'username': 'admin','machines': ['linux1','centos1','windows10']} >>> y = x.copy() >>> y['username'] = 'mlh' >>> y['machines'].remove('linux1') >>> y {'username': 'mlh', 'machines': ['centos1', 'windows10']} >>> x {'username': 'admin', 'machines': ['centos1', 'windows10']}

可以看到,在副本中替换值的时候,原始字典不受影响,但是如果修改了某个值(原地修改),原始字典也会改变。

其实因为浅克隆拷贝的只是引用,替换值就是替换成了其他引用,是不会影响原始字典的。而修改值,修改的是同一个引用指向的内存空间,因此都会影响。

避免这个问题的一种方法就是深克隆:

复制代码
1
2
3
4
5
6
7
8
9
10
11
>>> from copy import deepcopy >>> d = {} >>> d['names'] = ['Alfred','Bertrand'] >>> c = d.copy() >>> dc = deepcopy(d) >>> d['names'].append('Clive') >>> c {'names': ['Alfred', 'Bertrand', 'Clive']} >>> dc {'names': ['Alfred', 'Bertrand']}
  • fromkeys

使用给定的键建立新的字典,每个键对应的值默认为None,也可以指定:

复制代码
1
2
3
4
5
6
>>> {}.fromkeys(('name','age')) {'age': None, 'name': None} >>> {}.fromkeys(('name','age'),'unknown')#指定'unknown' {'age': 'unknown', 'name': 'unknown'}
  • get
    get访问字典中不存在的项时不会出错,并返回None。
复制代码
1
2
3
4
5
6
7
8
>>> people {'rose': 25, 'jack': 36} >>> print people.get('a') None >>> people.get('rose') 25

还可以自定义默认值,当不存在时返回默认值:

复制代码
1
2
3
4
>>> people.get('a',10) 10
  • items和iteritems

items方法将字典的所有项以列表的方式返回:

复制代码
1
2
3
4
>>> people.items() [('rose', 25), ('jack', 36)]

iteritems方法返回一个迭代器对象:

复制代码
1
2
3
4
5
6
7
>>> it = people.iteritems() >>> it <dictionary-itemiterator object at 0x1baa7e0> >>> list(it) #将迭代器转换为list [('rose', 25), ('jack', 36)]
  • pop
    获取给定键的值,并将这项从字典中移除

  • popitem

弹出随机的项(键值对)

  • setdeafult

在不含有给定键的情况下设值,存在给定键则返回对应的值。

复制代码
1
2
3
4
5
6
7
8
9
>>> d = {} >>> d.setdefault('name','None') 'None' >>> d {'name': 'None'} >>> d.setdefault('name','N/A') 'None'
  • update
    可以用一个字典项更新另外一个字典:
复制代码
1
2
3
4
5
6
7
>>> people {'rose': 25, 'jack': 36} >>> x = {'jack':24} >>> people.update(x) >>> people {'rose': 25, 'jack': 24}

x中如果有people中不存在的项,也会更新进去:

复制代码
1
2
3
4
5
6
7
8
>>> people {'rose': 25, 'jack': 24} >>> x = {'jack':21,'cherry':20} >>> people.update(x) >>> people {'rose': 25, 'jack': 21, 'cherry': 20}

映射的弹性键查询

有时候为了方便,就算某个键在映射(这里指字典)里不存在,我们也希望能获取一个默认值。此时有两个途径可以实现这个目的,一是通过defaultdict这个类型而不是普通的dict,另一个是自定义dict的子类,在子类中实现__missing__方法。

defaultdict

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys import re import collections WORD_RE = re.compile(r'w+') index = collections.defaultdict(list) #以list构造方法作为default_factory来创建一个defaultdict with open(sys.argv[1],encoding='utf-8') as fp: for line_no,line in enumerate(fp,1): for match in WORD_RE.finditer(line):#返回索引从1开始的序列,值为文件中的行 word = match.group() column_no = match.start() + 1 location = (line_no,column_no) index[word] .append(location) #如果index中没有word的记录,那么会返回一个空列表 for word in sorted(index,key=str.upper): print(word,index[word])

以当前文件作为参数传入,结果输出如下(部分结果):

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
append [(16, 26)] argv [(10, 15)] as [(10, 41)] coding [(1, 7)] collections [(5, 8), (9, 9)] column_no [(14, 13), (15, 33)] compile [(7, 14)] defaultdict [(9, 21)] encoding [(10, 23)] enumerate [(11, 25)] finditer [(12, 30)] for [(11, 5), (12, 9), (18, 1)] fp [(10, 44), (11, 35)] group [(13, 26)] import [(3, 1), (4, 1), (5, 1)] in [(11, 22), (12, 19), (18, 10)] index [(9, 1), (16, 13), (18, 20), (19, 16)] ...

所有这一切背后的功臣其实是魔法方法__missing__,它会在defaultdict遇到找不到的键时调用default_factory

__missing__

虽然dict没有定义这个方法,但是它是知道这个方法的存在的。如果某个类继承了dict,然后提供了__missing__方法,那么在__getitem__找不到键的时候,就会自动调用它。

我们定义一个在查询的时候,将映射类型里面的键转换str的类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# -*- coding: utf-8 -* class StrKeyDict0(dict): def __missing__(self, key): if isinstance(key,str): #如果找不到的key本身就是字符串,则抛错 raise KeyError(key) return self[str(key)] #否则转换成字符串再查找 def get(self, key, default=None): try: return self[key] #把查找用self[key]的形式委托给__getitem__ except KeyError: return default def __contains__(self, key): return key in self.keys() or str(key) in self.keys() if __name__ == '__main__': d = StrKeyDict0([('2','two'),('4','four')]) print(d['2'])#two #print(d[1]) #KeyError: '1' print(d.get('2'))#two print(d.get(1,'N/A'))#N/A

字典的变种

上面我们学习了dictdefaultdict,接下来学习其他的映射类型。

  • collections.OrderedDict 在添加键的时候会保持顺序,因此键的迭代顺序是一致的(类似Java中的LinkedHashMap)。popitem([last=True|False])返回字典里最后|第一个被添加进去的元素。
  • collections.ChainMap 可以容纳数个不同的映射对象,然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。相当于是一个字典的集合。
  • collections.Counter 给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列表对象计数。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [2]: import collections In [3]: ct = collections.Counter('abcdabcdabcard') In [4]: ct Out[4]: Counter({'a': 4, 'b': 3, 'c': 3, 'd': 3, 'r': 1}) In [5]: ct.update('aaaaaazzz') In [6]: ct Out[6]: Counter({'a': 10, 'b': 3, 'c': 3, 'd': 3, 'r': 1, 'z': 3}) In [7]: ct.most_common(2) # 返回出现次数最多的两个键和计数 Out[7]: [('a', 10), ('b', 3)]
  • colllections.UserDict 这个类其实就是把标准 dict 用纯 Python 又实现了一遍,主要是提供给用户来扩展的。

子类化UserDict

我们来改进上面的StrKeyDict0类,使得所有的键都存储为字符串类型。

要注意的是UserDict并不是dict的子类,但它有一个名为datadict实例属性(这里有点体现了组合优于继承)。这样做的好处是,UserDict的子类就能在实现__setitem__时避免不必要的递归,也可以让__contains__里的代码更简洁。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -* import collections class StrKeyDict(collections.UserDict): def __missing__(self, key): if isinstance(key,str): #如果找不到的key本身就是字符串,则抛错 raise KeyError(key) return self[str(key)] #否则转换成字符串再查找 def __contains__(self, key): return str(key) in self.data # def __setitem__(self, key, value): self.data[str(key)] = value #该方法会把所有的键都转换成字符串 if __name__ == '__main__': d = StrKeyDict([('2','two'),('4','four')]) print(d['2'])#two #print(d[1]) #KeyError: '1' print(d.get('2'))#two print(d.get(1,'N/A'))#N/A

不可变字典类型

有时候我们需要不可变的字典类型,比如在返回某个字典给用户,但又不想让用户修改该字典。从Python3.3开始,types模块中引入了一个封装类名叫MappingProxyType,如果给该类一个映射,它会返回一个只读的映射视图。虽然是只读的,但是能观察到原映射的修改。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [1]: from types import MappingProxyType In [2]: d = {1:'A'} In [3]: d_proxy = MappingProxyType(d) In [4]: d_proxy Out[4]: mappingproxy({1: 'A'}) In [5]: d_proxy[2] = 'x' --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-5-bc17a9a62754> in <module> ----> 1 d_proxy[2] = 'x' TypeError: 'mappingproxy' object does not support item assignment In [6]: d[2] = 'B' In [7]: d_proxy Out[7]: mappingproxy({1: 'A', 2: 'B'})

集合

集合是由可迭代对象构造的,可用于去重。
集合是可变的,但本身只能包含不可变(可散列)的值。

复制代码
1
2
3
>>> set([0,1,2,3,0,1,2,3,4,5]) set([0, 1, 2, 3, 4, 5])

还可以通过字面量来构造:

复制代码
1
2
3
4
5
6
7
8
9
10
In [21]: s = {1} In [22]: type(s) Out[22]: set In [23]: s Out[23]: {1} In [24]: s.pop() Out[24]: 1 In [25]: s Out[25]: set()

{1,2,3}这种字面量相比set([1,2,3])要更快且更易读。

可以求并集和交集:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> a = set([1,2,3]) >>> b = set([2,3,4]) >>> a.union(b) set([1, 2, 3, 4]) >>> a | b #交集 set([1, 2, 3, 4]) >>> c = a & b #并集 >>> c set([2, 3]) >>> c.issubset(a) True >>> c <= a True >>> c.issuperset(a) False >>> c >= a False >>> a.intersection(b) set([2, 3]) >>> a & b set([2, 3]) >>> a.difference(b) # 差集 set([1]) >>> a - b set([1])

可以看到,python重载了很多运算符可以很方便的操作set集合。

集合是可变的,也是不可变的集合类型frozenset

复制代码
1
2
3
In [34]: frozenset(range(10)) Out[34]: frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})

集合推导

Python2.7带来了集合推导和字典推导。

复制代码
1
2
3
4
5
6
In [37]: from unicodedata import name In [42]: {chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i),'')} # 新建一个 Latin-1 字符集合,该集合里的每个字符的Unicode 名字里都有“SIGN”这个单词 Out[42]: {'§', '=', '¢', '#', '¤', '<', '¥', 'μ', '×', '$', '¶', '£', '©', '°', '+', '÷', '±', '>', '¬', '®', '%'}

集合的操作

在这里插入图片描述

上图中列出了可变和不可变集合所拥有的方法的概况,其中不少方法是运算符重载的特殊方法。

在Python3中,key()items()values()方法返回的都是字典视图,它们不可修改同时可以实时反馈字典的变化。

setfrozenset的实现依赖散列表,它们在散列表中只存放键而没有相应的值。(这点和Java的Set实现类似)

最后

以上就是幸福煎饼最近收集整理的关于Python中的字典与集合创建和使用字典字典的变种子类化UserDict不可变字典类型集合集合推导集合的操作的全部内容,更多相关Python中内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(76)

评论列表共有 0 条评论

立即
投稿
返回
顶部