流畅的Python读书笔记(字典、集合)

流畅的Python读书笔记(字典、集合)

先上总体框架(学习交流)流畅的Python读书笔记(字典、集合)_第1张图片
一、范映射类型

流畅的Python读书笔记(字典、集合)_第2张图片流畅的Python读书笔记(字典、集合)_第3张图片
这个地方不需要特别注意,因为在实际操作中过程中,我们可以再查看相关文档来补。

可散列对象可以简单地理解为:值不会发生改变的对象就是可散列的对象。散列值就是__hash__方法返回的值。注意区分hash和id的区别。具体参考
链接: hash和id的区别.
二、字典的推导
这里话不多说直接上代码。
(1)字典的简单构造

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’,2)])
e = dict({‘three’:3,‘one’:1,‘two’:2})
流畅的Python读书笔记(字典、集合)_第4张图片

(2)字典的推导

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

coutry_code = {
     country:code for code ,country in DIAL_CODES}
Country = {
     code:country.upper() for country , code in coutry_code.items() if code < 66}

下面是执行效果
流畅的Python读书笔记(字典、集合)_第5张图片
一是一个可迭代的对象,country_code和Country的构造方法也是类似于列表的构造方法,最后的那一步(if code < 66) 可以用其他函数进行操作。

三、常见的映射方法
流畅的Python读书笔记(字典、集合)_第6张图片
以下是这三种来自collections的对比(*表示该类型中含有)
流畅的Python读书笔记(字典、集合)_第7张图片
流畅的Python读书笔记(字典、集合)_第8张图片
重点来了!!!!!
在字典中用常用a[‘a’]来进行寻找键值对,找不到时就会抛出异常。或许有经验的人都会采取使用a.get(k,default)来在找不到该键时,进行一个新键的引入,但是这都不是效率最高的方法。来看以下几个示例。

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):#依次迭代可迭代对象返回(0,数)
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start()+1#(正则匹配字符串的位置)
            location = (line_no, column_no)
            # 较为普通的做法
            occurrences = index.get(word, [])  # <1>
            occurrences.append(location)       # <2>
            index[word] = occurrences          # <3>

# print in alphabetical order
for word in sorted(index, key=str.upper):  # <4>key本身没有被调用,而是在输出时调用,相当于字符串的格式化
    print(word, index[word])

但其实上面的1,2,3步骤都可以用一行代码搞定

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)  # <1>先寻找word键找不到就放一个新键,匹配一个[],最后再向list中添加数据

# print in alphabetical order
for word in sorted(index, key=str.upper):
    print(word, index[word])

上面(1)的写法类似于

my_dict.setdefault(key,[]).append(new_value)
#或者下面这样
if key not in my_dict:
	my_dict[key]=[]
my_dict[key].append(new_value)
#这种方法需要进行两次键的寻找

如果我们在平时只是单纯进行查询取值而不是通过查找来插入新值。接着往下看

四、映射的弹性键查询
两个方法:
(1)defaultdict处理找不到的键的一个选择

import sys
import re
import collections

WORD_RE = re.compile(r'\w+')

index = collections.defaultdict(list)     # <1>这里进行初始化,在找不到的时候进行list操作
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[word].append(location)  # <2>如果index中没有word的记录,那么便会设置一个[],并将这个返回,所以append的操作一直都会成功
for word in sorted(index, key=str.upper):
    print(word, index[word])

在上面的方法中,如果不进行list的初始化,那么查询不存在的键的时候还是会抛出keyerror的错误。
注意:
defaultdict只会在__getitem__中被调用,也就是a[b]这个操作,才会被调用,如果使用get方法,那么没有什么屌用,会返回None.

(2)_missing__方法的奇思妙用
其实这这一切都是这个方法的神奇使用罢了。在这些对象中,当我们找不到键的时候,都会调用这个方法。打个比方,如果有一个对象继承了dict,并且重新定义了这个方法,那么在找不到这个键的时候,就会按照其自定义的方式进行运行。
注意:
该方法也是只会被a[b]方法所调用。对get还有_contains
(in运算符会调用)没有什么影响。

class StrKeyDict0(dict):  # <1>继承

    def __missing__(self, key):
        if isinstance(key, str):  # <2>判断寻找的键是不是str,若没有这一行,missing方法会陷入无限的递归循环中,而且确保了寻找是不是str类型的该键,更加完整的查找
            raise KeyError(key)
        return self[str(key)] 

    def get(self, key, default=None):
        try:
            return self[key]  # <3>将工作委托给getitem方法
        except KeyError:
            return default  # <5>

    def __contains__(self, key):
        return key in self.keys() or str(key) in self.keys()  # <6>先判断原键,再判断字符串类型的

第六步中,如果采用key in my_dict,也会导致无限递归调用,所以我们还是采用了keys方法。

五、常用的几种字典变种
流畅的Python读书笔记(字典、集合)_第9张图片
(1)ChainMap的代码解释
流畅的Python读书笔记(字典、集合)_第10张图片
流畅的Python读书笔记(字典、集合)_第11张图片
在这里插入图片描述
这种动态对象用着也是超级爽的。
链接: ChainMap分析.

(2)Counter对象代码
流畅的Python读书笔记(字典、集合)_第12张图片
在这里插入图片描述
超级好用的most_common方法,返回映射里最常见的n个键和他们的计数。

六、子类化UserDict
在我们进行重写新的映射对象的时候,我们通常使用该类,而不是继承dict,因为后者在实现的时候很多对的方法都使用小路,导致我们还需要继续改写,麻烦。
还有一个值得注意的地方,但是我还没有搞懂,截取原文,希望大家明白。
UseDict有一个data的属性,是dict的示例,这个属性是UserDict最终存储数据的地方。我们用这个子类来重写上面的StrKeyDict类。

import collections


class StrKeyDict(collections.UserDict):  # <1>

    def __missing__(self, key):  # <2>
        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, item):#即update方法,即在更新中使用,同时也在初始化中使用
        self.data[str(key)] = item   # <4>所有键都转换为了字符串了,具体实现也已经委托给self.data

七、不可变的映射对象
流畅的Python读书笔记(字典、集合)_第13张图片
流畅的Python读书笔记(字典、集合)_第14张图片
相当于重新构造了一个只读对象,要进行修改,需要对原对象进行修改才可以。

八、集合论
集合这个概念在高中数学就已经有了详细介绍,这里我只是进行简单的叙述,以及代码验证。
集合的本质是许多唯一对象的聚集,因此集合本身是可以用于去重。
流畅的Python读书笔记(字典、集合)_第15张图片
集合中的元素必须是可散列的,set类型本事是不可散列的,但是frozenset可以。

集合中的骚操作

#needles的元素在haystack里出现的次数,两个变量都是set类型
needles = {
     1,2,3,4}
haystack = {
     2,3,5,6}
print('needles的元素在haystack里出现的次数,两个变量都是set类型')
print(len(needles & haystack))
#如果不使用交集操作的话,可以使用以下
"""
foud = 0
for n in needles:
    if n in haystack:
     found += 1"""

#另外一种操作可以使任何可迭代对象needles 和 haystack上。
#found = len(set(needles) & set(haystack) )

#found = len(set(needles).intersection(haystack))   haystack已经是集合。
#只要双方有一个是集合对象,那么下面这两个方法就比上面的高效。

(1)集合的字面量
注意
如果要创建一个空集,那么应该采用不带任何参数的set()而不是使用{}。因为后者的方法优先创建一个字典。
流畅的Python读书笔记(字典、集合)_第16张图片
其次,在集合的构造上,set([1,2,3])的方法远没有直接使用{}方法来得直接。
流畅的Python读书笔记(字典、集合)_第17张图片
可以通过反汇编函数dis直接看出来。但是前面提到过的frozenset则没有这种好用的构造方式。我们既然说到构造上了,继续看一下集合的推导。
(2)集合推导
在这里插入图片描述
具体的语法关系和前面列表的推导和字典的推导没有什么太大的差别。下面我们就来看一下集合的骚操作。
(3)集合的操作
注意frozenset的区别哦!!!

下面这个是几个超类之间的关系
流畅的Python读书笔记(字典、集合)_第18张图片
这一个是对数学运算符的重载,可以一一对应
流畅的Python读书笔记(字典、集合)_第19张图片

下面这些返回的值都是bool值
流畅的Python读书笔记(字典、集合)_第20张图片
还有这些简单的操作,希望大家在使用的时候进行查找
流畅的Python读书笔记(字典、集合)_第21张图片

上面就是集合的字典的一些操作和叙述。我主要是删减掉了一些书中叙述得特别难的部分,更加注重了实际操作的运用,后面还有两节关于散列表的分析,我这里就不加过多的叙述,我通过链接到其他位置,大家进行查看,我看完之后,说句实话对我的使用影响不是很大,但是这部分的知识可能就是这样,更多的都是常在自己的心里,只有用到的时候,我们才知道当初的努力是多么的重要,码字,敲码还是花了几个小时,希望对大家的学习有一点点的帮助,我之后将和cookbook交叉阅读,并进行知识点的梳理。

我将dict和set的特点总结如下:(速度是以内存为代价的)
1.其中的元素必须是可散列的
2.很消耗内存
3.可以高效的判断元素是否在这些对象里面
4.元素的次序往往取决于添加的顺序
5.但是后续添加可能会打乱顺序,这个和散列表大小扩容有关
链接:dict和set的实现

https://github.com/fluentpython/example-code
https://item.jd.com/12186192.html?utm_source=xcx.ubja.vip&utm_medium=jingfen&utm_campaign=t_1001829303_&utm_term=78d7b3246a4247088b48f9df82fa2680

你可能感兴趣的:(学习笔记,python)