资讯专栏INFORMATION COLUMN

流畅的python读书笔记-第三章Python 字典与集合

lvzishen / 1169人阅读

摘要:小总结标准库里的所有映射类型都是利用来实现只有可散列的数据类型才能用作这些映射里的键值不用字典推导用处理找不到的键找不到键返回某种默认值底层是与调用实现的字典插入更新原理其他大多数映射类型都提供了两个很强大的方法和。

字典和集合

标准库里的所有映射类型都是利用 dict 来实现的
只有可散列的数据类型才能用作这些映射里的键(值不用)

可散列

一个对象是可散列的

它的散列值是不变的

对象需要实现 __hash__() 方法

可散列对象还要有 __qe__() 方法

字典推导
DIAL_CODES = [(86, "China"), (91, "India"), (1, "United States"), (62, "Indonesia") ]

country_code = {country: code        for code, country in DIAL_CODES     }
结果
{"China": 86, "India": 91, "United States": 1, "Indonesia": 62}
常见的映射方法 page137 用setdefault处理找不到的键
##找对应的key,没有的话返回默认值
my_dict = {"name":"longe","age":8}
my_dict.setdefault("namerrr","default")

print(my_dict)

用 setdefault 只需要一次就可以完成整个操作。

defaultdict找不到键返回某种默认值

在实例化一个 defaultdict 的时候

这个可调用对象会在 getitem 碰到找不到的键的时候被调用,

getitem 返回某种默认值。

实现方式

defaultdict 里的 default_factory 只会在__getitem__ 里被调用

比如,dd 是个 defaultdict,k 是个找不到的键,

dd[k] 这个表达式会调用 default_factory 创造某个默认值,

dd.get(k) 则会返回 None。

原理
所有这一切背后的功臣其实是特殊方法 __missing__。
它会在defaultdict 遇到找不到的键的时候调用 default_factory
__missing__这个方法

自定义一个映射类型,更合适的策略其实是继承collections.UserDict 类

只是为了演示 missing 是如何被dict.__getitem__ 调用的。

class StrKeyDict0(dict):
    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()
isinstance(key, str) 测试在上面的__missing__ 中是必需的
但是如果 str(k) 不是一个存在的键,代码就会陷入无限递归。

这是因为 missing 的最后一行中的 self[str(key)] 会调用 __getitem__,
而这个 str(key) 又不存在,于是 __missing__又会被调用。

精简版本

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, item):
        self.data[str(key)] = item
setitem 会把所有的键都转换成字符串。由于把具体的实现委
托给了 self.data 属性,这个方法写起来也不难
字典的变种
collections.OrderedDict

这个类型在添加键的时候会保持顺序,因此键的迭代次序总是一致
的。

collections.ChainMap

该类型可以容纳数个不同的映射对象,然后在进行键查找操作的时
候,这些对象会被当作一个整体被逐个查找,直到键被找到为止。

collections.Counter

这个映射类型会给键准备一个整数计数器。每次更新一个键的时候
都会增加这个计数器。

colllections.UserDict

这个类其实就是把标准 dict 用纯 Python 又实现了一遍。
跟 OrderedDict、ChainMap 和 Counter 这些开箱即用的类型不
同,UserDict 是让用户继承写子类的。下面就来试试。

集合论 集合推导
from unicodedata import name

aa = {chr(i) for i in range(32, 256) if "SIGN" in name(chr(i), "")}
print(aa)
集合的数学运算 page161 字典空间

因为 Python 会设法保证大概还有三分之一的表元是空的,所以在快要达

到这个阈值的时候,原有的散列表会被复制到一个更大的空间里面。

散列表原理

为了获取 my_dict[search_key] 背后的值

Python 首先会调用hash(search_key) 来计算 search_key 的散列值,

把这个值最低的几位数字当作偏移量

在散列表里查找表元(具体取几位,得看当前散列表的大小

若找到的表元是空的,则抛出 KeyError 异常。

若不是空的,则表元里会有一对 found_key:found_value。

这时候 Python 会检验 search_key == found_key 是否为真,如 果它们相等的话,就会返回found_value。

如果 search_key 和 found_key 不匹配的话,这种情况称为散列 冲突。

原理图

添加新元素和更新现有键值

添加新元素和更新现有键值的操作几乎跟上面一样。
只不过对于前者,在发现空表元的时候会放入一个新元素;
对于后者,在找到相对应的表元后,原表里的值对象会被替换成新值。

优劣

字典浪费存储空间(不过没有几百万对象,内存好几个G不用考虑)
dict 的实现是典型的空间换时间:字典类型有着巨大的内存开销

键的次序取决于添加顺序

当往 dict 里添加新键而又发生散列冲突的时候,新键可能会被安
排存放到另一个位置。

注意:

无论何时往字典里添加新的键,Python 解释器都可能做出为字典扩

容的决定。

扩容导致的结果就是要新建一个更大的散列表,并把字典里已有的元素添加到新表里。

这个过程中可能会发生新的散列冲突,导致新散列表中键的次序变化。

要注意的是,上面提到的这些变化是否会发生以及如何发生,都依赖于字典背后的具体实现,

因此你不能很自信地说自己知道背后发生了什么。

如果你在迭代一个字典的所有键的过程中同时对字典进行修改,那么这个循环很有可

能会跳过一些键——甚至是跳过那些字典中已经有的键。

更新字典的主要使用姿势

由此可知,不要对字典同时进行迭代和修改。

如果想扫描并修改一个字典,最好分成两步来进行:

首先对字典迭代,以得出需要添加的内容,把这些内容放在一个新字典里;

迭代结束之后再对原有字典进行更新。

小总结:

标准库里的所有映射类型都是利用 dict 来实现

只有可散列的数据类型才能用作这些映射里的键(值不用)

字典推导

用setdefault处理找不到的键

defaultdict找不到键返回某种默认值

底层是 getitem 与__miss__调用实现的

字典插入更新原理!!!

其他

大多数映射类型都提供了两个很强大的方法:setdefault 和

update。

setdefault 方法可以用来更新字典里存放的可变值(比如列

表),从而避免了重复的键搜索。

update 方法则让批量更新成为可能,它可以用来插入新值或者更新已有键值对,它的参数可以是包含(key, value) 这种键值对的可迭代对象,或者关键字参数。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41625.html

相关文章

  • 流畅python读书笔记-第一章Python 数据模型

    摘要:第一章数据类型隐式方法利用快速生成字典方法方法通过下标找元素自动支持切片操作可迭代方法与如果是一个自定义类的对象,那么会自己去调用其中由你实现的方法。若返回,则会返回否则返回。一个对象没有函数,解释器会用作为替代。 第一章 python数据类型 1 隐式方法 利用collections.namedtuple 快速生成字典 import collections Card = coll...

    FullStackDeveloper 评论0 收藏0
  • 流畅python读书笔记-第一章Python 数据模型

    摘要:第一章数据类型隐式方法利用快速生成类方法方法通过下标找元素自动支持切片操作可迭代方法与如果是一个自定义类的对象,那么会自己去调用其中由你实现的方法。若返回,则会返回否则返回。一个对象没有函数,解释器会用作为替代。 第一章 python数据类型 1 隐式方法 利用collections.namedtuple 快速生成类 import collections Card = collec...

    tomener 评论0 收藏0
  • 流畅python读书笔记-第五章 一等函数

    摘要:可以通过定位参数和关键字参数传入的形参多数函数的参数属于此类。就像数据格式化一样数据带上标签自行创建函数它会自行创建函数。创建的函数会在对象上调用参数指定的方法自己创建函数冻结参数这个高阶函数用于部分应用一个函数。 高阶函数 接受函数为参数,或者把函数作为结果返回的函数是高阶函数 def reverse(word): return word[::-1] ...

    546669204 评论0 收藏0
  • 流畅python读书笔记-第二章Python 数据结构

    摘要:把具名元组以的形式返回,我们可以利用它来把元组里的信息友好地呈现出来。数组支持所有跟可变序列有关的操作,包括和。双向队列和其他形式的队列类双向队列是一个线程安全可以快速从两端添加或者删除元素的数据类型。 列表表达式 >>> symbols = $¢£¥€¤ >>> codes = [ord(symbol) for symbol in symbols] >>> codes [36, 16...

    syoya 评论0 收藏0
  • 流畅python读书笔记-第七章-函数装饰器和闭包

    摘要:函数装饰器和闭包严格来说,装饰器只是语法糖。何时执行装饰器它们在被装饰的函数定义之后立即运行。装饰器突出了被装饰的函数的作用,还便于临时禁用某个促销策略只需把装饰器注释掉。 函数装饰器和闭包 严格来说,装饰器只是语法糖。如前所示,装饰器可以像常规的可调用对象那样调用,其参数是另一个函数。有时,这样做更方便,尤其是做元编程(在运行时改变程序的行为)时。 Python何时执行装饰器 它们在...

    Hydrogen 评论0 收藏0

发表评论

0条评论

lvzishen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<