dict的abc继承关系
python中的dict属于mapping类型,在collections中可以查找到“Mapping”和“MutableMapping(可修改的Mapping,继承自Mapping)”,准确的讲dict属于后者。
1 2 3 4 |
from collections.abc import Mapping a = {} print(isinstance(a, MutableMapping)) #这里的True并不是说a这个字典继承了MutableMapping,而是其实现了MutableMapping中的魔法方法,isinstance方法的运作原理在于MutableMapping中有“MutableMapping.register(dict)”操作,将dict注册到MutableMapping中,实质是使用了hook方法。 |
dict的常用方法
在pycharm中可以点击查看dict的源码,如上文所述,dict使用C语言编写,在pycharm中看到的dict的内部接口只是以代码的方式展示其文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
a = {"user1": {"name": "Tom"}, "user2": {"name": "Jack"} a.clear() #清空字典 b=a.copy() #返回字典的“浅拷贝”,后续修改b会影响到a import copy c = copy.deepcopy(a) #返回字典的“深拷贝”,完全独立,修改c不会影响a new_dict= dict.fromkeys(可迭代类型,默认值) #直接利用fromkeys()对可迭代类型给默认值生成字典 iterlist = ["a", "b"] adict = dict.fromkeys(iterlist, {"name": "xxx"}) a.setdefault("user3", "Katty") #对未查找到的user3直接赋值,并返回user3的值,是dict.get()方法某些应用场景的增强版 a.update({"user4":"Sam"}) #update用来扩展dict,接收一个可迭代对象,所以有很多其他方式 a.update(user5="LingLing", user6= "XiaoMing") a.update([("user7", "James"]) a.update((("user8", "Wade"),)) |
dict的子类
在开发中是可以对python的内置数据结构(如列表list、字典dict)做继承的,但是不建议继承这些使用C语言实现的数据结构,原因在于继承后重载父类的方法在某些情况下可能不生效,如下例。上述问题的解决方法是使用python的collections模块中的UserDict,该类通过python模拟C语言将所有内部逻辑都实现了一遍,通过继承该类解决不生效的问题。
1 2 3 4 5 6 7 8 9 |
class Mydict(dict): def __setitem__(self, key, value): super().__setitem__(key, value*2) my_dict = Mydict(one= 1) #这种情况重载的__setitem__不生效 print(my_dict ) my_dict["one"]= 1 #这种情况重载的__setitem__生效 print(my_dict ) from collections import UserDict |
python内置的dict的子类:defaultdict,也是使用C语言实现的。其实现的方法原理是:在UserDict中的__getitem__方法有逻辑是如果查找不到key,则调用“__missing__”魔法函数,在该函数中会调用default_factory()[是一个property]给该key设置一个默认值。利用上述属性,defaultdict才有了如下使用:
1 2 3 |
from collections import defaultdict my_dict = defaultdict(dict) #在这里可以设置默认值 value=my_dict["user1"] #返回的value即使我们设置的默认值,空字典"{}" |
set和frozenset
set即集合,frozenset即不可变集合。set作为一种无序集合,最大的特点是“不重复”,所以主要用于去重、判断是否包含。frozenset因为其不可变且不重复,所以可以作为dict的key。集合操作和数学运算相关,常见的有求集合的差集、交集等等,同时由于python的set和dict都是使用C语言实现的,且都使用了hash的实现原理,性能非常高,做查找运算的时间复杂度为O(1)。
1 2 3 4 |
s= set("abcd") ss = frozenset("abcde") s1= set("cd") re_set = s.difference(s1) #返回存在于s中但不存在于s1中的元素集合 |
dict和set的实现原理
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 |
#dict和list性能测试 from random import randint def load_list_data(total_nums, target_nums): """ 从文件中读取数据,以list的方式返回 :param total_nums: 读取的数量 :param target_nums: 需要查询的数据的数量 """ all_data = [] target_data = [] file_name = "fbobject_idnew.txt" with open(file_name, encoding="utf8", mode="r") as f_open: for count, line in enumerate(f_open): if count < total_nums: all_data.append(line) else: break for x in range(target_nums): random_index = randint(0, total_nums) if all_data[random_index] not in target_data: target_data.append(all_data[random_index]) if len(target_data) == target_nums: break def load_dict_data(total_nums, target_nums): """ 从文件中读取数据,以dict的方式返回 :param total_nums: 读取的数量 :param target_nums: 需要查询的数据的数量 """ all_data = {} target_data = [] file_name = "fbobject_idnew.txt" with open(file_name, encoding="utf8", mode="r") as f_open: for count, line in enumerate(f_open): if count < total_nums: all_data[line] = 0 else: break all_data_list = list(all_data) for x in range(target_nums): random_index = randint(0, total_nums-1) if all_data_list[random_index] not in target_data: target_data.append(all_data_list[random_index]) if len(target_data) == target_nums: break return total_times/ test_times def find_test(all_data, target_data): #测试运行时间 test_times = 100 total_times = 0 import time for i in range(test_times): find = 0 start_time = time.time() for data in target_data: if data in all_data: find += 1 last_time = time.time() - start_time total_times += last_time return total_times/test_times if __name__=="__main__": # all_data, target_data = load_list_data(10000, 1000) # all_data, target_data = load_list_data(100000, 1000) # all_data, target_data = load_list_data(1000000, 1000) # all_data, target_data = load_dict_data(10000, 1000) # all_data, target_data = load_dict_data(100000, 1000) # all_data, target_data = load_dict_data(1000000, 1000) all_data, target_data = load_dict_data(2000000, 1000) last_time = find_test(all_data, target_data) #dict查找的性能远远大于list #在list中随着list数据的增大,查找时间会增大 #在dict中查找元素不会随着dict的增大而增大 print(last_time) |
- dict的key或者set的值,都必须是可以hash的。不可变对象都是可hash的, str, fronzenset, tuple,自定义的hash类(实现__hash__)
- dict的内存花销大,但是查询速度快, 自定义的对象 或者python内部的对象都是用dict包装的
- dict的存储顺序和元素添加顺序有关
- 添加数据有可能改变已有数据的顺序