栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

读书笔记:《流畅的Python》第3章 字典和集合

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

读书笔记:《流畅的Python》第3章 字典和集合

"""
dict广泛应用于模块的命名空间实例的属性函数的关键字参数
它的内置函数都放在__builtins__.__dict__模块中
散列表是字典背后的数据结构
集合的实现也依赖散列表
"""

"""本章内容大纲
    1.常见的字典方法
    2.如何处理查找不到的键
    3.标准库中dict的变种
    4.set和frozenset类型
    5.散列表的工作原理
    6.散列表的潜在影响
        什么样的数据类型可作为键
        不可预知的顺序"""

# 3.1 泛映射类型
import collections.abc
"""Mapping
MutableMapping
这两个抽象基类的作用:
    为dict和其他类似的类型定义形式接口
    作为形式化的文档,他们定义了构建一个映射类型所需要的最基本的接口
    可以跟isinstance判断某个数据是不是广义的映射类型"""
# 例:
# my_dict = dict()
# print(isinstance(my_dict, collections.abc.Mapping))
# >>>True

# tips:可散列的数据类型
"""如果一个对象时可散列的,那么在这个对象的生命周期中,它的散列值是不变的
    需要实现__hash__方法
    需要实现__eq__方法,和其他键作比较,
        如果两个可散列对象相等,他们的散列值是一样的
    
    可散列对象:
        原子不可变数据类型都是可散列的:
            str/bytes/数值类型
        frozenset
        元组
            所有元素都为可散列的,元组才是可散列的  
    
"""
# 例
# tt = (1,2,(30,40))
# print(hash(tt))  # >>>-3907003130834322577
# tl = (1,2,[30,40])
#  hash(tl) # 报错 列表不可散列
# tf = (1,2,frozenset([30,40]))
# print(hash(tf)) # 5149391500123939311


# 一般来讲,所有用户创建的对象都是可散列的,散列值就是id()的返回值
# x = (100,)
# print(hash(x))
# print(id(x))
# >>>2279747396317938166
# >>>2411106468240
# 经过验证不同

# 创建字典的不同方式

# 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,'two':2,'one':1})
# print(a == b == c == d == e)  #  >>>True



# 3.2字典推导式(dict comprehensive)

# 将一个装元组的列表变成两个不同的字典

# DIAL_CODES = [
#     (86,'China'),
#     (91,'India'),
#     (1,'USA'),
#     (62,'Indonesia'),
#     (55,'Brazil'),
#     (92,'Pakistan'),
#     (880,'Bangladesh'),
#     (234,'Nigeria'),
#     (7,'Russian'),
#     (81,'Japan')
# ]
# country_code = {country:code for code,country in DIAL_CODES}
# print(country_code)
#
# country_code_upper = {code:country.upper() for country,code in country_code.items()
#                       if code < 66}
# print(country_code_upper)

# 3.3 常见的映射方法

# 用setdefault处理找不到的键
#     index0.py
#     index.py

# 3.4映射的弹性键查询
'''某个键在映射里不存在
但希望通过这个键读取值的时候,能得到一个默认值
2种方法:
    1.使用defaultdict
    2.定义dict的子类,实现__missing__方法'''

    # 3.4.1 defaultldict 处理找不到的键的一个选择

'''在用户创建defaultdict对象的时候,需要给构造方法提供一个可调用对象
    这个可调用对象会在__getitem__碰到找不到的键的时候被调用
    让__getitem__返回某种默认值
    而这个用来生成默认值的可调用对象存放在default_factory的实例属性中'''
# 例子
# from collections import defaultdict
# dd = defaultdict(list)
# print(dd.default_factory) # 查看实例属性
# print(dd['one'])
'''
dd['one']过程分析:
    1.调用list()来建立一个新列表
    2.把这个新列表作为值,'one'作为它的键,放到dd中
    3.返回这个列表的引用'''

# index_default.py

    # 3.4.2 特殊方法__missing__
# 这个方法会在映射类型处理找不到的键的时候触发
# 示例 StrKeyDict0.py

# 3.5 字典的变种
import collections

# collections.OrderedDict
"""
这个类型在添加键的时候会保持顺序,因此键的迭代次序总是一致的
popitem方法默认删除并返回字典最后一个元素
my_odict.popitem(last = False)删除并返回第一个被添加的元素"""

# my_odict = collections.OrderedDict([(1,'one')])
# my_odict.update([(2,'two'),(3,'three')])
# my_odict.setdefault(4,'four')
# print(my_odict.get(4,'键不存在'))
# print(my_odict)
# print(my_odict.popitem())
# print(my_odict.popitem(last=False))

# collections.ChainMap
"""该类型可以容纳几个不同的映射对象
在进行键查找操作时,这些对象会被当做一个整体被逐个查找,直到键被找到为止
这个功能在给有嵌套作用域的语言做解释器的时候很有用"""
# import builtins
# pylookup = collections.ChainMap(locals(),globals(),vars(builtins))
# print(pylookup)

# collections.Counter
"""这个类型会给键准备一个整数计速器,每次更新一个键的时候都会增加这个计数器
作用:给可散列对象计数
或者当成多重集合来使用,集合的元素可以出现不止一次
实现了+,-运算符来合并记录
most_common([n])会按次序返回最常见的n个键和他们的计数"""
# ct = collections.Counter('ababacedbderasdfssd')
# print(ct)
# ct.update('asdjkhfhjkasdgfasnbdjkasdnblsdgfbajsdfsagudjkashnfjksdhsdk')
# print(ct)
# ct1 = collections.Counter('sadjkasklddfh')
# print(ct-ct1)
# print(ct.most_common(3))

# collections.UserDict
"""这个类型其实是把标准的dict用纯python又实现了一遍
它是让用户继承写子类的"""

# 3.6子类化UserDict
# UserDict不是dict的子类,但是有一个data属性,是dict的实例,这个属性实际上是最终存储数据的地方
# 示例:StrKeyDict.py

# 3.7不可变映射类型
# 标准库中没有,types模块中有一个MappingProxyType类
# 会返回一个只读的映射视图,无法通过视图对原映射做修改

# from types import MappingProxyType
# d = {1:'A'}
# d_proxy = MappingProxyType(d)
# print(d_proxy)
# print(d_proxy[1]) # d中的内容可以通过d_proxy看到
# # d_proxy[2] = 'x' # 报错,不能修改
# d[2] = 'B' # 对d 的修改会反馈到d_proxy上
# print(d_proxy)
# print(d_proxy[2])
# print(d)

# 3.8集合论
"""集合的本质是许多唯一对象的聚集,因此可以用于去重
集合的元素必须是可散列的
中缀运算符: 两个集合a,b
        a | b 合集
        a - b 差集
        a & b 交集"""

# 3.8.1集合字面量
"""
空集:set()
"""
# from dis import dis  # 反汇编函数
# dis('{1}')
# dis('set([1])')

"""
  1           0 LOAD_ConST               0 (1)  # 检查{1}字面量背后的字节码
              2 BUILD_SET                1      # 特殊的BUILD_SET几乎完成了所有工作
              4 RETURN_VALUE
  1           0 LOAD_NAME                0 (set) # set([1])的字节码
              2 LOAD_ConST               0 (1)
              4 BUILD_LIST               1
              6 CALL_FUNCTION            1
              8 RETURN_VALUE"""

# python没有frozenset的字面量表示法,只能采用构造方法
# print(frozenset(range(1,10)))


# 3.8.2集合推导

"""from unicodedata import name
set1 = {chr(i) for i in range(32,256) if 'SIGN' in name(chr(i),'')}
print(set1)
print([name(chr(i),'') for i in range(32,256)])"""

# 3.8.3集合的操作
"""集合运算:
    两个集合a,b
        a | b 合集
        a - b 差集
        a & b 交集
    比较运算符:
     e in a 
     a > b />=
     a < b /<=
     其他方法:
     a.add(e) :把3添加到集合a中
     a.clear() : 清空集合
     a.copy() : 返回a的浅复制
     a.discard(e) : 如果e存在,移除它
     a.pop()
     a.remove()"""

# a = {1,2,3,4}
# a.discard(5)  # 不会报错
# a.remove(5)  # KeyError

# 3.9 dict和set的背后
"""
dict 和 set 的效率有多高?
为什么他们是无序的?
为什么并不是所有的python对象都可以作为dict的键或者set的元素?
为什么dict的键和set的元素的顺序是根据他们被添加的次序决定的,在映射对象的生命周期中,这个顺序并不是一成不变的
为什么不应在迭代循环dict或者set的时候往里面添加元素?"""

# 3.9.1 效率

# 3.9.2 字典中的散列表
# 内置的hash()方法可以用于所有的内置类型对象

# 3.9.3 dict的实现及其导致的结果
"""键必须是可散列的
一个可散列对象必须满足以下要求:
1.支持hash()函数,并且通过__hash__得到的散列值是不可变的
2.支持通过__eq__来检测相等性
3.若 a==b,则hash(a) == hash(b)
4.字典在内存上开销巨大
5.如果需要存放数量巨大的记录,那么放在元组或者具名元组构成的列表中会是比较好的选择
6.键查询很快
7.键的次序取决于添加顺序
    由dict([(key1,value1),(key2,value2)])
    和dict([(key2,value2),(key1,value1)])得到的两个字典
    比较的时候是相等的,但如果在key1和key2被添加的字典的时候有冲突发生
    这两个键出现在字典里的顺序是不一样的"""

# 例
# DIAL_CODES = [
#     (86,'China'),
#     (91,'India'),
#     (1,'USA'),
#     (62,'Indonesia'),
#     (55,'Brazil'),
#     (92,'Pakistan'),
#     (880,'Bangladesh'),
#     (234,'Nigeria'),
#     (7,'Russian'),
#     (81,'Japan')
# ]
# d1 = dict(DIAL_CODES)
# print('d1:', d1.keys())
# d2 = dict(sorted(DIAL_CODES))
# print('d2:', d2.keys())
# d3 = dict(sorted(DIAL_CODES,key=lambda x:x[1]))
# print('d3:', d3.keys())
# assert d1 == d2 and d2 == d3 # 这些字典是相等的,因为包含的数据是一样的

"""结果:
d1: dict_keys([86, 91, 1, 62, 55, 92, 880, 234, 7, 81])
d2: dict_keys([1, 7, 55, 62, 81, 86, 91, 92, 234, 880])
d3: dict_keys([880, 55, 86, 91, 62, 81, 234, 92, 7, 1])"""

# 往字典里添加新键可能会改变已有键的顺序
#     因为添加新键可能涉及字典扩容
#     因此不要在迭代循环一个字典的时候修改字典


# 3.9.4 set的实现以及导致的结果
"""
1.集合中的元素必须是可散列的
2.集合痕消耗内存
3.可以痕高效地判断元素是否存在于集合中
4.元素的次序取决于添加到集合的次序
5.往集合里添加元素,可能会改变集合里已有元素的次序"""

# 3.10 总结
"""
1.字典是python的基石
2.除了dict外,标准库还提供好用的映射类型 defaultDict OrderedDict Counter ChainMap
3.以及便于用户扩展的UserDict
4.大多数映射类型都提供两个痕强大的方法 update 和 setdefault
5.映射类型的API中有个很好用的__missing__方法,用于对象找不到某个键的时候,通过这个
    方法自定义会发生什么
6.collections.abc提供两个抽象基类 Mapping MutableMapping
    利用他们可以进行类型查询或者引用
7.types模块中的MappingProxyType可以用来创建不可变映射类型
8.dict和set高效率的背后是因为使用了散列表作为数据结构
    同时提高效率的代价是以牺牲空间换来的"""

index0.py

'''创建一个从单词到其出现情况的映射'''
import sys
import re
WORD_RE = re.compile(r'w+')
index = {}
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):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no,column_no)
            occurrences = index.get(word,[])
            occurrences.append(location)
            index[word] = occurrences
# 以字母顺序打印结果
for word in sorted(index,key = str.upper):
    print(word,index[word])

'''控制台执行的部分结果                                                   
six [(7, 1), (7, 5), (7, 9), (7, 13), (7, 17), (7, 21), (7, 26)]
ten [(12, 1), (15, 1), (18, 1)]
three [(3, 1), (3, 7), (3, 13)]
two [(2, 1), (2, 5), (2, 9)]'''

index.py

'''创建一个从单词到其出现情况的映射'''
import sys
import re
WORD_RE = re.compile(r'w+')
index = {}
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):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no,column_no)
            # 如果单词不存在,把单词和一个空列表放进映射,然后返回这个空列表
            index.setdefault(word,[]).append(location)
# 以字母顺序打印结果
for word in sorted(index,key = str.upper):
    print(word,index[word])

index_default.py

'''创建一个从单词到其出现情况的映射'''
import sys
import re
from collections import defaultdict
WORD_RE = re.compile(r'w+')
index = 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):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no,column_no)
            # 如果在创建defaultdict时没有指定default_factory,查询不存在的键会触发KeyError
            index[word].append(location)
# 以字母顺序打印结果
for word in sorted(index,key = str.upper):
    print(word,index[word])

 

mappingproxytype.py
from types import MappingProxyType
d = {1:'A'}
d_proxy = MappingProxyType(d)
print(d_proxy)
print(d_proxy[1])
# print(d_proxy[2])
d[2] = 'B'
print(d_proxy)
print(d_proxy[2])
StrKeyDict0.py
# 实现一个dict的子类,在查询的时候把非字符串的键转换成字符串

class StrKeyDict(dict): # 继承了dict
    # 实现__missing__方法为找不到的键提供默认值
    def __missing__(self,key):
        if isinstance(key,str): # 如果找不到的键本身就是字符串,抛出异常
            raise KeyError(key)
        return self[str(key)]  # 如果找不到的键不是字符串,那么转化成字符串再查找

    def get(self,key,default = None):
        try:
            return self[key]
        except KeyError:
            return default

    def __contains__(self, key):
        return key in self.keys() or str(key) in self.keys()

if __name__ == '__main__':
    sd = StrKeyDict([('2','two'),('4','four')])
    print(sd['2'])
    print(sd[4])
    # print(sd[1])
    print(sd.get('2'))
    print(sd.get(4))
    print(sd.get(1, 'N/A'))
    print(2 in sd)
    print(1 in sd)
StrKeyDict.py
# 实现一个collections.UserDict的子类,无论是添加更新还是查询操作
# 都会把非字符串的键转化为字符串

import collections

class StrKeyDict(collections.UserDict): #
    # 实现__missing__方法为找不到的键提供默认值
    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

if __name__ == '__main__':
    sd = StrKeyDict([('2','two'),('4','four')])
    print(sd['2'])
    print(sd[4])
    # print(sd[1])
    print(sd.get('2'))
    print(sd.get(4))
    print(sd.get(1, 'N/A'))
    print(2 in sd)
    print(1 in sd)

35岁学Python,也不知道为了啥!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/487629.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号