Fluent_Python_Section2数据类型,03-dict-set,字典和集合

字典和集合

dict和set都基于hash table实现

1. 大纲:

  1. 常见的字典方法
  2. 如何处理查找不到的键
  3. 标准库中dict类型的变种
  4. setfronzenset类型
  5. Hash table的工作原理
  6. Hash table带来的潜在影响

字典dict

2. 泛映射类型

在collections.abc中,有Mapping和MutableMapping,两个抽象基类,主要作用是作为形式化文档,定义了映射类型的基本API。

my_dict = {}
#判定数据是不是广义上的映射类型
#不用type的原因是这个参数可能不是dict
isinstance(my_dict, abc.Mapping)

标准库里的映射类型都是基于dict扩展的,因此它们有个共同的限制,只有hashable的数据类型才能作为key(保持键唯一),值没有这个限制。

https://docs.python.org/3/glossary.html#term-hashable

Hashable对象要实现__hash__()和__eq__()方法

一般来说不可变类型都是可hashtable的,但是有特例,dict虽然是不可变类型,但它里面的元素可能是可变的类型。

tt = (1, 2, (30, 40))
hash(tt)

#Error, dict里面的list不是可散列的类型
t1 = (1, 2, [30, 40])
hash(t1)

tf = (1, 2, frozenset([30, 40]))
hash(tf)

创建字典的不同方式

>>> a = dict(one=1, two=2, three=3)
>>> b = {‘one‘: 1, ‘two‘: 2, ‘three‘: 3}
>>> c = dict(zip([‘one‘, ‘two‘, ‘three‘], [1, 2, 3]))
>>> d = dict([(‘two‘, 2), (‘one‘, 1), (‘three‘, 3)])
>>> e = dict({‘three‘: 3, ‘one‘: 1, ‘two‘: 2})
>>> a == b == c == d == e
True

3. 字典推导式(dict comprehension, dictcomp)

列表推导式用[],元组推导式用(),字典推导式用{}

dictcomp可以从任何以key-value作为元素的可迭代元素中构建出字典

DIAL_CODES = [
    (86, ‘China‘),
    (91, ‘India‘),
    (1, ‘United States‘),
    (62, ‘Indonesia‘),
    (55, ‘Brazil‘),
    (92, ‘Pakistan‘),
    (880, ‘Bangladesh‘),
    (234, ‘Nigeria‘),
    (7, ‘Russia‘),
    (81, ‘Japan‘),
]

#dictcomp, {}
country_code = {country : code for code, country in DIAL_CODES}

print(country_code)

#以大写打印code < 65的国家名
temp = {code : country.upper() for country, code in country_code.items()}
print(temp)

4. 常见的映射类型方法

中文电子书P137,dict、collections.defaultdict和collections.OrderedDict的常用方法

5. 用setdefault处理找不到的键(key)

d[key]找不到时会抛出KeyError异常,可以用d.get(key, default)来代替,给找不到的key一个默认的返回值。但是要更新某个key对应的value时,用__getitem__和get都是不自然,而且效率低。所以d.get并不是处理找不到的key的最好方法

中文电子书P139,用setdefault处理

dict1 = {‘name‘:‘Allen‘, ‘age‘:18}
#setdefault方法如果有key-value就不动,没有就添加。这个方法有返回值
print(dict1)

dict1.setdefault(‘age‘, 30)
dict1.setdefault(‘xxx‘, 22)
print(dict1)

6. 用defaultdict处理找不到的键 未搞懂

7. 用__missing__处理找不到的键

不只字典,所有映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。

That is, 如果一个类继承了dict,然后这个继承类提供了__missing__方法,那么在__getitem__碰到找不到的键的时候,Python就会自动调用它,而不是抛出KeyError异常。

Note:__missing__方法只会被__getitem__调用,例如d[key]。另外对get或contains(in运算符用到这个方法)没有影响。

像 k in my_dict.keys()在Python3中效率是很高的,因为dict.keys()返回的是dictionary-view-objects。而在Python2中,dict.keys()返回的是一个列表,k in my_list操作需要扫描整个列表。

8. 字典的变种

不同的映射类型:

  1. collections.OrderedDict: 在添加键的时候会保持顺序。
  2. collections.ChainMap:可以容纳数个不同的映射对象,在进行键查找操作的时候,这些对象会被当作一个整体被逐个查找。这个特性在给有嵌套作用域的语言做解释器时非常有用,可以用一个映射对象来代表一个作用域的上下文。
  3. collections.Counter这个映射类型可以给hashable的对象计数,或者是当作多重集来用。Counter实现+和-运算符来合并记录,还有most_common([n])会按照次序返回映射里最常见的n个键和它们的计数。
from collections import Counter

str1 = ‘aabbccdd‘

counter_str = Counter(str1)
print(type(counter_str))
print(counter_str)

counter_str.update(‘aabbccdd‘)
print(counter_str)
print(counter_str.most_common(3))

4.collections.UserDict,这个类把标准dict用纯Python实现了一遍。让用户继承来写子类的。

9. 自定义映射类型

所有映射类型都是基于dict的。自定义映射类型,以UserDict为基类,比普通的dict为基类方便。

因为如果从dict继承,dict有时候在某些方法上走一些捷径,导致子类要重写这些方法,但UserDict不会有这些问题。具体看中文电子书12.1节。

Note:UserDict并不是dict的子类,是MutableMapping的子类,但是其中有一个叫data的属性是dict的实例,这个是UserDict存储数据的地方。好处是UserDict的子类实现__setitem__和__contains__时代码更简洁。

例子1. 添加、更新还是查询操作,StrKeyDict都会把非字符串的键转换为字符串

import collections

class StrKeyDict(collections.UserDict):

    def __missing__(self, key):
        if isinstance(key, str):
            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

StrKeyDict里剩下的映射类型的方法都是从UserDict、MutableMapping和Mapping这些超类继承而来。特别是Mapping这个ABC(抽象基类),以文档化的形式提供了实用的API。以后两个方法值得注意:

  1. MutableMapping.update。可以直接利用,可以用在__init__里。原理是self[key] = value来添加新值,所以它是使用__setitem__方法。
  2. Mapping.get。

10. 不可变映射类型

之前有不可变的序列类型,但是在标准库中没有不可变的字典类型,但是可以用替身来代替。

字典是可以动态修改的,但不希望用户修改,所以需要一个只读的映射视图。types.MappingProxyType返回一个只读的映射视图。

from types import MappingProxyType

d = {‘1‘ : ‘A‘}
d_proxy = MappingProxyType(d)
print(‘d_proxy:‘, d_proxy)
#MappingProxyType只读,没有赋值操作
#d_proxy[2] = ‘x‘
d[2] = ‘x‘
#MappingProxyType是动态的,也就是对d所做的任何改动都会反馈到它上面。
print(‘d_proxy:‘, d_proxy)

集合set

11. 集合

set的本质是许多唯一对象的聚集。因此,集合可以去重

l1 = [‘spam‘, ‘spam‘, ‘eggs‘, ‘eggs‘]
s1 = set(l1)
print(s1)
print(list(s1))

set中的元素必须是hashable(保证唯一),但set本身是不可散列的,但是frozenset是hashable的。因此可以创建一个包含不同frozenset的set。set里面的元素是hashable的,所以搜索速度极快。

set还有很多基础的中缀运算符,a | b并集,a & b交集,a - b差集。可以省去不必要的循环和逻辑操作。

例子1. needles元素在haystack里出现的次数,两个变量都是set类型

found = len(needles & haystack)

例子2. needles和haystack是任何两个可迭代对象

found = 0
for n in needles:
    if n in haystack:
        found += 1

例子3. 转换为set再运算

found = len(set(needles) & set(haystack))

#另一种写法
found = len(set(needles).intersection(haystack))

12. 集合字面量

{1}, {1, 2}是集合的字面量,set()是空集, {}是空字典。

字面量{1,2,3}比构造方法(set([1,2,3]))更快。集合字面量,Python会利用BUILD_SET的字节码来创建集合。

from dis import dis
dis(‘{1}‘)

dis(‘set([1])‘)

创建frozenset只能用构造方法

frozenset(range(10))

13. 集合推导式(set comprehension, setcomps)

列表推导式用[], 元组推导式用(), 字典和集合推导式用{}

例子1. 用setcomps创建一个Latin-1字符集合

#获取字符的名字name
from unicodedata import name
#把编码32~255之间的字符的名字里有"SIGN"单词挑出来,放到一个集合里面
s1 = {chr(i) for i in range(32, 256) if ‘SIGN‘ in name(chr(i),‘‘)}

print(s1)
print(name(‘+‘,‘‘))

14. 集合的操作

collections.abc(抽象基类),提供API信息。

集合的数学运算: 中文电子书P161

集合的比较运算: 中文电子书P162

集合的其他方法: 中文电子书P163

15. dict和set的原理

dict和set都是借助hash table来实现功能的。例如in运算,所以速度快。

Note:列表的背后没有用散列表来支持in运算符,每次搜索都是顺序遍历。

16. 字典中的散列表

如果对象A == 对象B,那么hash(A) == hash(B)。调用hash(),实际上运行的是__hash__。

dict取值原理采用散列表算法,中文电子书P169

17. 使用散列表实现dict带来的优势和限制

中文电子书P171

  1. key必须是hashable的
  2. 字典在内存上的开销巨大,因为hash table是稀疏数组
  3. key查询很快,因为hash table是classic的空间换时间
  4. key的顺序取决于添加顺序
  5. 往字典里添加新键可能会改变已有键的顺序,不要对字典同时进行迭代和修改。因为Python解释器可能做出为字典扩容的决定,把旧表复制到一个新的更大的散列表时,可能发生散列冲突。

    6. .keys()、.items()、.values()返回的是字典的视图,动态反馈字典的变化。

18. 使用散列表实现set带来的优势和限制

set和frozenset的实现也依赖散列表,但在它们的hash table存放的只有元素的引用(就像在字典里只存key而没有相应的值)。

Note:在set加入Python前之前,我们都是把字典加上无意义的value当集合使用。

和散列表实现dict的优点和缺点类似:

  1. 集合里的元素必须是可散列的。
  2. 集合很消耗内存。
  3. 可以很高效地判断元素是否存在于某个集合中。
  4. 元素的次序取决于被添加到集合里的次序。
  5. 往集合里添加元素,可能会改变集合里已有元素的次序。

原文地址:https://www.cnblogs.com/allen2333/p/8724813.html

时间: 2024-07-30 14:24:20

Fluent_Python_Section2数据类型,03-dict-set,字典和集合的相关文章

Learing-Python【7】:Python数据类型(3)—— 字典、集合

一.字典类型 1.用途:用来存放多个不同种类的值 2.定义方式:在{ }内用逗号分隔开多个key:value的元素,其中value可以是任意数据类型,而key的功能通常是用来描述value的,所以key通常是字符串类型,但其实key必须是不可变的类型(如:int.float.str.tuple) 3.常用操作+内置方法 优先掌握的操作: 1)按key存取值:可存可取 2)len:长度 3)in.not in:成员运算 4)del.pop.popitem del:所有类型的删除方式 pop:删除一

第6天数据类型之元组,字典,集合

元组(不可变,有序,多个值) 元组类型就是一个不可变的列表,为什么会有元组呢?因为在我们写程序的过程中可能会出现这样的一个情况,也就是为了防止一些不必要的bug,一些重要的数据别人是可以读取的,但是不能够进行更改,这个时候我们就需要用到元组了.对于列表而言,python中对于元祖的存储相对来说更为存储空间的.(因为它不必再像列表一样维护更改操作了) 既然元组本身就是一个不可变的列表了,那它的除了修改列表的操作之外的一些操作和列表是完全一样的,此处就不再陈述了,但是此处我要说的是元组的不可变究竟指

Python3组合数据类型(元组、列表、集合、字典)语法

一.序列类型(字符串,元组(),列表[]) 序列类型支持in,len(),分片[],迭代,5种内置序列类型:bytearray,bytes,list,str,tuple(元组). 1.元组可以嵌套(如:x=str[2][1][0][1]) 2.元组的命名(collections.namedtuple(),即自定义) 样:sale=collctions.namedtuple("sale","productid customerid date price") 逗号前的为

python基础知识——基本的数据类型2——列表,元组,字典,集合

磨人的小妖精们啊!终于可以归置下自己的大脑啦,在这里我要把--整型,长整型,浮点型,字符串,列表,元组,字典,集合,这几个知识点特别多的东西,统一的捯饬捯饬,不然一直脑袋里面乱乱的. 一.列表 1.列表的全部方法 如:['1','2'].['wupeiqi', 'alex'] 1 >>> dir(list) 2 ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '_

基本数据类型(字符串_数字_列表_元祖_字典_集合)

基本数据类型(字符串_数字_列表_元祖_字典_集合) 1.字符串 2.数字 除了布尔类型外,int.long.float和complex都可以使用的运算为:加.减.乘.除.整除.幂运算和取余 3.列表和元组 列表的内容可变,可以包含任意对象,使用中括号表示.元组的内容不可变,可以包含任意对象,使用圆括号表示.元组 1 l = [1, 2, 3, '4', '5'] # 列表 2 l = list((1, 2, 3, '4', '5')) 3 4 t = (1, 2, 3, '4', '5') #

python----基础之数据类型(元祖,字典,集合)

元祖 元祖的定义和特性 定义:列表已经介绍过,现在介绍一种与类表相似的类型,叫做元祖,只不过把[]改成(). 特性: 1.可以存放多个指 2.不可变 3.按照从左往右的顺序定义元祖的元素,下标从0开始依次顺序访问,有序 元祖的创建与常用类型 1 # 创建 2 >>> we = (11, 22, 33, 44, 55) 3 >>> we 4 (11, 22, 33, 44, 55) 5 >>> type(we) 6 <class 'tuple'&g

组合数据类型练习,英文词频统计实例上列表,元组,字典,集合的遍历。 总结列表,元组,字典,集合的联系与区别。

1.字典实例:建立学生学号成绩字典,做增删改查遍历操作. d={'天':95,'青':78,'色':65,'等':66}print('学生成绩字典:',d)d['烟']=98print('增加:',d)d.pop('等')print('删除:',d)d['天']=78print('修改:',d)print('查询青成绩:',d.get('青','无')) 2.列表,元组,字典,集合的遍历.总结列表,元组,字典,集合的联系与区别. s=list('123456789')t=set('7564231

python数据类型——列表、元组、字典、集合

python中的序列,是一块用来存储多个值的连续内存空间,类似于C语言中的数组 常用的序列结构有列表.元组.字典.字符串.集合等, 无序序列:字典.集合 有序序列:列表.元组.字符串,支持双向索引(第一个元素下标为0,最后一个元素下标为-1) 不可变序列:字符串.元组 可变序列:列表.集合.字典 一.列表 尽量从列表尾增加或删除元素,可以大幅度提高处理速度 列表中的元素可以不相同 列表常用方法,见下表,可参考https://www.cnblogs.com/huangping/p/7730347.

老齐python-基础4(元祖、字典、集合)

1.元祖 元祖的特性结合了字符串和列表 元祖是用圆括号括起来的,其中的元素之间用逗号(英文半角)隔开.元祖中的元素是任意类型的python对象(包括以后自定义的对象) 元祖中的元素不可更改,所以修改列表的方法,在元祖中都会失效.     使用场景: 元祖比列表操作速度快.如果定义了一个值,并且唯一要用他做的是不断的遍历他,那么请使用元祖代替列表. 如果对不需要修改的数据进行"写保护",即该数据是常量,那么此时也要使用元祖.如果必须要改变这些值,则可以转换为列表修改. 元祖可以在字典(又