非常详细的Python字典高级用法介绍(三)

1、子类应该继承collections.UserDict而非dict

在(用法二)里面讲过,创建新的映射类型或自定义字典类型,最好扩展collections.UserDict,而不是dict。为了确保以str类型存储添加到映射中的键,我们在(用法二)中定义了DemoDict类时就已经提到过一嘴。

子类最好继承UserDict的主要原因是,内置的dict在实现上走了一些捷径,如果继承dict,那就不得不覆盖一些方法,而是继承UserDict则没有这些问题。

注意的是,UserDict没有继承dict,使用的是组合模式:内部有一个dict实例,名为data,用于存放具体的项。与(用法二)里面的案例相比,可以避免__setitem__等特殊方法意外递归,还能简化__contains__的实现。

from collections import UserDict


class DemoDict(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

 2、不可变映射

标准库提供的映射类型都是可变的,不过有时也有防止用户意外更改的映射。types模块提供的MappingProxyType是一个包装类,把传入的映射包装成一个mappingproxy实例,这是原映射的动态代理,只可读取。这意味着,对原映射的更新将体现在mappingproxy实例上,但是不能提供mappingproxy实例更改映射。

from types import MappingProxyType

dict1 = {1: "A", 2: "B"}
dict_proxy = MappingProxyType(dict1)
print(dict_proxy)  # {1: 'A', 2: 'B'}
print(dict_proxy[1])  # A
dict_proxy[3] = "C"  # 不能提供dict_proxy更改映射,TypeError: 'mappingproxy' object does not support item assignment
dict1[3] = "C"
print(dict_proxy)  # {1: 'A', 2: 'B', 3: 'C'}

3、字典视图

dict的实例方法keys(),values()和item()分别返回dict_keys、dict_values和dict_items类的实例。这些字典视图是dict内部实现使用的数据结构的只读投影。Python2中对应的方法返回列表,重复dict中已有的数据,有一定内存开销。另外,视图还取代了返回迭代器的旧方法。

d = dict(a=1, b=2, c=3)
print(d.keys())  # dict_keys(['a', 'b', 'c'])
print(d.values())  # dict_values([1, 2, 3])
print(len(d))  # 3
print(list(d.values()))  # [1, 2, 3]
print(reversed(d.values()))  # 视图自定义__reversed__方法,返回一个自定义的迭代器 <dict_reversevalueiterator object at 0x000001E561B3E930>
print(d.values()[0])  # TypeError: 'dict_values' object is not subscriptable

4、dict的实现方式对实践的影响

Python 使用哈希表实现 dict,因此字典的效率非常高,不过这种设计对实践也有⼀些影响,不容忽视。

  • 键必须是可哈希对象,必须正确实现__hash__和__eq__方法
  • 通过键访问项速度非常快,对于一个包含数百万个键的dict对象,Python通过计算哈希码就可以直接定位键,然后找出索引在hash表中的偏移量,稍微尝试几次就可以找到匹配的条目,因此开销不大
  • 在CPython3.6中,dict的内存布局更为紧凑,顺带的一个副作用是键的顺序得以保留。Python3.7正式支持保留顺序
  • 尽管采用了新的布局方式,但是字典仍然占用大量内存,这是不可避免的。对容器来说,最紧凑的内部数据结构是指向项的指针的数组(元组就是这样存储的)。与之相比,哈希表中的条目存储的数据更多,而且为了保证效率,Python至少需要把哈希表中三分之一的行留空。
  • 为了节省内存,不要在__init__方法之外创建实例属性

 

 

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值