Python——用setdefault处理找不到的键

注:对《流畅的python》第三章,用setdefault处理找不到的键,的解析

当字典 d[k] 不能找到正确的键的时候,Python 会抛出异常。
也许可以用 d.get(k, default) 来代替 d[k],给找不到的键一个默认的返回值(这比处理 KeyError 要方便不少)。但是要更新某个键对应的值的时候,不管使用__getitem__还是 get都会不自然,而且效率低。就像示例 3-2 中的还没有经过优化的代码所显示的那样,dict.get并不是处理找不到的键的最好方法。

一般的解决方法

示例 3-2 这段程序从索引中获取单词出现的频率信
息,并把它们写进对应的列表里

#创建一个从单词到其出现情况的映射
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, [])
            #通过get函数,获得字典里word对应的值,如果没找到word(还没有记录),则返回[]
            occurrences.append(location) #将单词的位置加到位置list的后面
            index[word] = occurrences #将字典中word对应的列表进行更新
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper): 
    print(word, index[word])

示例 3-3 这里是示例3-2 的不完全输出,每一行的列表都代表一
个单词的出现情况,列表中的元素是一对值,第一个值表示出现的
行,第二个表示出现的列

$ python3 index0.py ../../data/zen.txt
a [(19, 48), (20, 53)]
Although [(11, 1), (16, 1), (18, 1)]
ambiguity [(14, 16)]
be [(15, 14), (16, 27), (20, 50)]
Beautiful [(3, 1)]
...

❶'\w+'正则表达式

正则表达式中的'\w+'
(1)对于Unicode(str类型)模式:匹配任何Unicode的单词字符,基本上所有语言的字符都可以匹配,当然也包括数字和下划线;如果开启了re.ASCII标志,就只匹配[a-zA-Z0-9]
(2)对于8位(bytes类型)模式:匹配ASCII中定义的字母数字,即[a-zA-Z0-9]
此处我觉得应该就是(2)

❷sys.argv

sys.argv是传递给Python脚本的命令行参数列表。argv[0]是脚本名称(依赖于操作系统,无论这是否是完整路径名)。如果使用-c解释器的命令行选项执行命令,argv[0]则将其设置为字符串' -c'。如果没有脚本名称传递给Python解释器,argv[0]则为空字符串。
举例

$ python hello.py
>>> print(sys.agrv)
['hello.py']
$ python hello.py world
>>> print(sys.argv)
['hello.py', 'world']
$ python hello.py world hello
>>> print(sys.argv)
['hello.py', 'world', 'hello']

with open(sys.argv[1], encoding='utf-8') as fp这句话就是以utf-8文件模式打开sys.argv[1]文件,将文件开头的位置传给fp参数

❸enumerate()函数

enumerate()函数用于将一个可遍历的数据对象(如字符串、元组、列表)组合成一个索引序列,同时列出数据下标和数据(一般用在for循环当中)

语法:enumerate(seq, [start =0])
seq:一个序列、迭代器或其他支持迭代对象
start:下标起始的位置(一般默认为0)

>>> number = ['one', 'two', 'three']
>>> list(enumerate(number))
[(0, 'one'), (1, 'two'), (2, 'three')]
>>> list(enumerate(number, start = 2))
[(2, 'one'), (3, 'two'), (4, 'three')]

#for 循环使用enumerate
>>> for i, element in enumerate(seq):
···     print i, element
···
0 one
1 two
2 three 

❹finditer()函数

re.finditer(pattern, string, flags = 0)
在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回

import re

it = re.finditer(r"\d+", "123abc456def789")
for match in it:
    print(match.group())

输出结果:

123
456
789

❺match.group()正则表达式

我们使用group(num)或groups()匹配对象函数来获取匹配表达式
group(num = 0):匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
groups():返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

❻正则表达式start()和end()

start([group])方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为0;
end([group])方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为0;


采用setdefault的更好的解决方法

示例 3-4 用dict.setdefault就解决了获取和更新单词的出现情况列表

#创建从一个单词到其出现情况的映射
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])

➊获取单词的出现情况列表,如果单词不存在,把单词和一个空列表放进映射,然后返回这个空列表,这样就能在不进行第二次查找的情况下更新列表了
也就是说,这样写

my_dict.setdefault(key, []).append(new_value)

与这样写

if key not in my_dict:
    my_dict[key] = []
    my_dict[key].append(new_value)

二者的效果是一样的,只不过后者至少要进行两次键查询——如果键不存在的话,就是三次,用setdefault只需要一次就可以完成整个操作。
(转载自https://blog.csdn.net/qq_43168521/article/details/103027871)

你可能感兴趣的:(Python——用setdefault处理找不到的键)