返回顶部

05 python深入python的set和dict

dict的abc继承关系

dict属于mapping类型,下面通过查看源码来分析它的继承关系

from collections.abc import Mapping, MutableMapping

查看MutableMapping源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class MutableMapping(Mapping):
 
    __slots__ = ()
 
    """A MutableMapping is a generic container for associating
    key/value pairs.
 
    This class provides concrete generic implementations of all
    methods except for __getitem__, __setitem__, __delitem__,
    __iter__, and __len__.
 
    """
 
    @abstractmethod
    def __setitem__(self, key, value):
        raise KeyError
 
    @abstractmethod
    def __delitem__(self, key):
        raise KeyError
 
    __marker = object()
 
    def pop(self, key, default=__marker):
        '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
          If key is not found, d is returned if given, otherwise KeyError is raised.
        '''
        try:
            value = self[key]
        except KeyError:
            if default is self.__marker:
                raise
            return default
        else:
            del self[key]
            return value
 
    def popitem(self):
        '''D.popitem() -> (k, v), remove and return some (key, value) pair
           as a 2-tuple; but raise KeyError if D is empty.
        '''
        try:
            key = next(iter(self))
        except StopIteration:
            raise KeyError
        value = self[key]
        del self[key]
        return key, value
 
    def clear(self):
        'D.clear() -> None.  Remove all items from D.'
        try:
            while True:
                self.popitem()
        except KeyError:
            pass
 
    def update(*args, **kwds):
        ''' D.update([E, ]**F) -> None.  Update D from mapping/iterable E and F.
            If E present and has a .keys() method, does:     for k in E: D[k] = E[k]
            If E present and lacks .keys() method, does:     for (k, v) in E: D[k] = v
            In either case, this is followed by: for k, v in F.items(): D[k] = v
        '''
        if not args:
            raise TypeError("descriptor 'update' of 'MutableMapping' object "
                            "needs an argument")
        self, *args = args
        if len(args) > 1:
            raise TypeError('update expected at most 1 arguments, got %d' %
                            len(args))
        if args:
            other = args[0]
            if isinstance(other, Mapping):
                for key in other:
                    self[key] = other[key]
            elif hasattr(other, "keys"):
                for key in other.keys():
                    self[key] = other[key]
            else:
                for key, value in other:
                    self[key] = value
        for key, value in kwds.items():
            self[key] = value
 
    def setdefault(self, key, default=None):
        'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D'
        try:
            return self[key]
        except KeyError:
            self[key] = default
        return default
 
MutableMapping.register(dict)

  

可以看到MutableMapping继承的是Mapping(不可变) ,最后把dict注册到MutableMapping.中   MutableMapping.register(dict)

可以通过 isinstance 查看一个字典的类型

1
2
3
4
5
from collections.abc import Mapping, MutableMapping
#dict属于mapping类型
 
a = {}
print (isinstance(a, MutableMapping))

测试结果如下

dict的常用方法

 

 copy(这里是浅拷贝)

1
2
3
4
5
6
a = {"biao1":{"company":"imooc"},
     "biao2": {"company": "imooc2"}
     }
#copy, 返回浅拷贝
new_dict = a.copy()
new_dict["bobby1"]["company"] = "imooc3"

调试结果如下 

 

fromkeys把可迭代的对象转变为dict

 

1
2
3
new_list = ["bobby1", "bobby2"]
 
new_dict = dict.fromkeys(new_list, {"company":"imooc"})

调试结果如下

get取值,可以设置一个默认值避免字典中keyerror异常

1
2
a = {"name1": "jack", "name2": "jane"}
value = a.get("name3", "not exist")

调试结果如下

 setdefault(),和get()相似,不同的是如果没有某个键名,会把此键名和默认值加入到字典中

1
2
3
4
5
a = {"name1": "jack", "name2": "jane"}
 
  
# 没有指定键名时
a.setdefault("name3", "not exist")

调试结果如下

update(), 可用于添加字典元素

 

1
2
3
4
5
6
a = {"name1": "jack", "name2": "jane"}
 
# 直接添加字典方式
a.update({"name3": "hong"})
# 使用参数名方式
a.update(name4="lilei", name5="mei")

调试结果如下

dict的子类

 如果我们想要继承dict重写我们的逻辑 ,不建议继承list和dict 因为他们是c语言写的,我们重写的函数有可能不会生效

1
2
3
4
5
6
7
8
9
10
#不建议继承list和dict
class Mydict(dict):
    def __setitem__(self, key, value):
        super().__setitem__(key, value*2)
 
my_dict = Mydict(one=1# 在这个时候没生效
print (my_dict)
 
my_dict["one"] = 1  # 在这个时候生效
print(my_dict)

打印结果

 

 应该使用python为我们提供的UserDict

1
2
3
4
5
6
7
8
from collections import UserDict
 
class Mydict(UserDict):
    def __setitem__(self, key, value):
        super().__setitem__(key, value*2)
 
my_dict = Mydict(one=1)
pass

调试结果如下

 

dict 的子类defaultdict

 当我们取值一个不存在的值的时候,它会返回一个 {}

1
2
3
4
from collections import defaultdict
 
my_dict = defaultdict(dict)
my_value = my_dict["bobby"]

  

 调试结果如下

内部源码分析

 

set和frozenset

初始化一个set

1
2
3
4
5
6
# 使用set关键字
s1 = set('abc')
# 使用{}
s2 = {'a', 'b'}
 
print(type(s1), type(s2))

 

向set中添加元素

1
2
3
s1 = set('abc')
s1.add('d')
print(s1) 

  

使用update()函数更新set

1
2
3
4
s1 = set('abc')
s2 = set('xy')
s1.update(s2)
print(s1)

  

difference()函数来求两个集合的差集

1
2
3
4
5
6
s1 = set('abc')
s2 = set('cd')
 
# 相当于s1 - s2
re_set = s1.difference(s2)
print(re_set)

 

set的数学运算

1
2
3
4
5
6
7
8
9
s1 = set('abc')
s2 = set('cd')
 
# 差集
print(s1-s2)
# 交集
print(s1 & s2)
# 并集
print(s1 | s2)

 issubset()判断集合A是否为集合B的子集

1
2
3
4
s1 = set('abc')
s2 = set('c')
 
print(s2.issubset(s1))

  

dict和set的实现原理

list和dict的性能比较

  1 dict的查找性能远大于list
  2 在list中随着list数据量的增大,查找相同数据量的时间也会增大
  3 在dict中随着dict数据量的增大,查找相同数据量的时间不怎么受影响

 dict原理小结

dict找一个值之所以快,是因为他会根据这个键,进行哈希获得偏移量,直接取值,时间复杂度为0(1)

  1)dict的key或者set的值都必须是可hash的,他们的实现原理相同。不可变对象都是可hash的,比如string, tuple, fronzenset
  2)dict的内存花销大,但是查询速度快;自定义的类中,只要加上魔法函数__hash__, 那么这个类就是可hash的
  3)dict的存储顺序和元素添加顺序有关,因hash值冲突的原因
  4)添加数据的时候有可能会改变已有数据的顺序:存储dict的时候,python会预先申请一段大于dict数据需求的连续内存空间,以减少hash冲突的概率,当添加数据量使得大于分配内存空间的1/3的时候,python就会另申请一个较大的内存空间,把原先的数据进行迁移,重新进行hash值的计算

 

posted @   Crazymagic  阅读(549)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示

目录导航