大道至简,知行合一。

Python高级编程和异步I/O并发编程笔记 5 深入python的set和dict

dict的abc继承关系

python中的dict属于mapping类型,在collections中可以查找到“Mapping”和“MutableMapping(可修改的Mapping,继承自Mapping)”,准确的讲dict属于后者。

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的内部接口只是以代码的方式展示其文档。

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语言将所有内部逻辑都实现了一遍,通过继承该类解决不生效的问题。

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才有了如下使用:

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)。

s= set("abcd")
ss = frozenset("abcde")
s1= set("cd")
re_set = s.difference(s1)    #返回存在于s中但不存在于s1中的元素集合

dict和set的实现原理

#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的存储顺序和元素添加顺序有关
  • 添加数据有可能改变已有数据的顺序

赞(0)
未经允许不得转载:北凉柿子 » Python高级编程和异步I/O并发编程笔记 5 深入python的set和dict
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址