首页 > 其他分享 >02-defaultdic类工作原理及应用实例(全网最全?含常值函数的创建)

02-defaultdic类工作原理及应用实例(全网最全?含常值函数的创建)

时间:2024-03-13 13:01:11浏览次数:21  
标签:02 defaultdict 函数 dic __ print 常值 defaultdic

经常看到说“优雅地书写代码”,总是没啥感觉,直到这两天认真查阅了defaultdic这个类的一些资料。原来,灵活的应用,真的可以这么优雅、简单。


1  简要描述

defaultdic是collections包中的一个扩展类,是内置dict类的子类。

这个类比较简单,除了重写了dict类的__miss__方法,增加了一个实例变量外,其它与dict类一致。(但这个类的官方文档写得比较晦涩,感觉不用看那个文档,也就是这篇文章的第3部分)

运行流程如下:

1、当查询到不存在的key时,defaultdic对象调用__miss__(key)类,并将这个key与第1个参数的返回值进行映射绑定

2、使用的第1个实参(必须是可调用对象,可以是 int、str、list 等内置函数,也可以是自定义函数)的返回值进行映射绑定,形成[键:值]对,追加到实例中。

tips:如果在创建实例时,没有对第一个参数赋值,则defaultdic与内置dict类一样,遇到新的键,仍然会触发KeyError异常。

2  dict和defaultdic要解决什么样的问题

  • dict:更偏向于处理“已知映射关系”
  • defaultdic:偏向于处理“未知映射关系”

2.1  dict类的不足和defaultdic类的优势

有一些问题,虽然可以用映射关系来进行描述,但它们又无法穷举所有的可能(比如:各种问答系统),或者列举更多可能既浪费金钱,又不便操作(比如:生鲜店新菜和它的价格)。

当用dict来表述这类问题时,出现新的情况(新的键值),系统无法立即返回正确的值,对这个问题,需要额外花时间,单独进行处理,显得不那么聪明(优雅)。


而defaultdic类,刚好能够解决这种,“无法避免,而且一定会用类似的方法去处理”的问题。

它的实例对象在查询到不存在的键key时,按一定的规则,生成一个默认值,与这个key绑定,从而创建新的映射关系,再添加到类对象中去。

  • 比如:客户提出的一个新问题,可以自动建立一个回复,叫“待解决”,再把这个问题作为key,与“待解决”建立映射关系,装入问答库中,之后再对所有“待解决”的问题进行处理。
  • 再比如:蔬菜店上新品种,可以自动建立一个价格,赋予0值,再把这个品种和价格建立映射关系,装入产品库,之后再对所有价格为0的商品,修改价格后上架。

3  应用场景示例及代码

3.0  基本原理示例

defaultdict接收一个类对象或函数对象,在取值时,如果不存在对应的key则返回对应的函数返回值或默认构造函数的实例对象: 

3.0.1 以内置函数为参数

from collections import defaultdict

dic_1 = defaultdict(int)
dic_2 = defaultdict(tuple)
dic_3 = defaultdict(list)
dic_4 = defaultdict(str)
dic_5 = defaultdict(set)

这个地方default_factory的参数是int(),tuple()...,当查询key不存在时,自动将key与这些函数的默认返回值,进行绑定,再添加到对象的元素中。

from collections import defaultdict

dic_1 = defaultdict(int)
dic_2 = defaultdict(tuple)
dic_3 = defaultdict(list)
dic_4 = defaultdict(str)
dic_5 = defaultdict(set)

print(dic_1['1'])  # 0
print(dic_2['2'])  # ()
print(dic_3['3'])  # []
print(dic_4['4'])  # 没有显示,空字符串
print(dic_5['5'])  # set()

 3.0.2 以自定义函数或类为参数

当以自定义函数为参数时,返回值不能是None,否则defaultdict与dict没有区别;

当以类为参数时,__init__()需要有返回值,不能是None。

from collections import defaultdict


class SaySomething:
    def __init__(self, res='hello'):
        self.res = res

    def __str__(self):
        return self.res

    def getSomething(self, thing='!'):
        return self.res + thing


def someFun(res=123):
    return res


dicFrom_class = defaultdict(SaySomething)
dicFrom_fun = defaultdict(someFun)

print(dicFrom_class['1'])  # hello
print(dicFrom_fun['2'])  # 123

print(dicFrom_class['1'].getSomething(' world!')) # hello world!

以类为参数时,生成了类实例,可以使用下标访问内置方法,比如最后一条语句。

tips:这个地方点到为止,实际运用中,能够产生很多奇思妙想。

3.1 例程1:使用list作为参数,轻松建立一对多的字典

3.1.1  使用defaultdict类

from collections import defaultdict

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
    d[k].append(v)

print(sorted(d.items()))
# 返回:[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

说明:

1、字典中的每个键key,首次出现时,还没有任何映射关系;

2、系统使用default_factory函数(此时是list),返回1个空列表

(此时,映射关系已形成,即:键与一个空列表);

3、然后,使用列表的append()方法,将值添加到空列表中,从而完成[键:值]映射;


4、 随后,当再次遇到相同键时,系统返回这个已有1个元素的列表。然后使用列表的append()方法,添加新的元素。

5、 这一操作方式与dict.setdefault()方法类似,但在运行效率上,更好一些。

3.1.2  使用dict.setdefault()方法,实现同样功能

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = {}
for k, v in s:
    d.setdefault(k, []).append(v)

print(list(d.items()))

# 返回:[('yellow', [1, 3]), ('blue', [2, 4]), ('red', [1])]

先不论效率问题,单从可读性上,使用defaultdict也会更好一些。

3.2  例程2:使用int作为参数,轻松计数

计算字符串中,某个字母的个数(真的是非常简单)

from collections import defaultdict

s = 'mississippi'
d = defaultdict(int)
for k in s:
    d[k] += 1

print(sorted(d.items()))

# 返回:[('i', 4), ('m', 1), ('p', 2), ('s', 4)]

3.3  例程3:利用集合特性,使用set作为参数,去除一对多关系中的重复值

from collections import defaultdict

s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
d = defaultdict(set)
for k, v in s:
    d[k].add(v)

print(sorted(d.items()))

#返回:[('blue', {2, 4}), ('red', {1, 3})]

3.4  例程4:使用lambda创建常值函数,按需生成缺省值

前面提到过,内置函数的常值如下:

int():0
tuple () :()
list(): []
str():'',空字符串
set():set(),空集合

而自定义函数的返回值也是固定的,记得吗?

defaultdic类的第一个参数,必须是可调用对象。

1、如果按一般的办法,无法向自定义函数传递参数;

到目前为止,我们创建defaultdic类对象的写法,都是:

d = defaultdic(fun),而不是d = defaultdic(fun())

2、无法传递参数,自然不可能在程序中,灵活改变缺省值

3、改变这一困境的办法就是使用lamda函数,让函数的返回值,也就是fun()跟fun一样,都是可调用对象。

from collections import defaultdict


def constant_factory(value):
    return lambda: value


d1 = defaultdict(constant_factory(12))
d2 = defaultdict(constant_factory('hello world'))

print(d1['1']) # 12
print(d2['str1']) # hello world

4  补充:官方文档(选择参考,可不看)

 官方文档链接:collections — Container datatypes — Python 3.12.2 documentation

4.1  定义

class collections.defaultdict(default_factory=None, /[, ...])

defaultdic类是内置字典类型的子类。实例化defaultdic类,返回一个类似字典的类型。

相对于dict类,它只是重写了一个函数,增加了一个可写实例变量,其它完全一致。

【tips:instance variable实例变量(只在实例对象中发生变化),相对于类变量class variable】


实例对象初始化时,第一个实参为default_factory属性提供初始值。默认情况下,它的值是None。其它参数,与普通的dict类一样,作为初始值,传递给类的构造函数(__init__),在实例化对象时使用。

4.2  重写的函数__missing__(key)

1、查询不存在的键值时,会调用这个函数。

2、这个函数与default_factory 属性值有关。

        初始化对象时:

        2.1 如果没有传入这个参数或参数值为None,就会触发KeyError异常;

        2.2 如果不为None,这个函数就会把查询的值作为【键】,并把default_factory的值作为【键值】,创建新的键值对,插入字典中。

也就是说,当使用defaultdic类初始化类对象时,如果没有传用一个可调用对象,作为第一个实参,那么它和普通的dict没有区别。

4.3  default_factory

实际是一个可调用对象,接收一个工厂函数作为参数, 可以是 int、str、list 等内置函数,也可以是自定义函数。工厂函数的返回值,就会传入__missing__(key)。

mark一下(需要后期加深):这个地方的用法非常灵活,包括了:类、类实例(实现了__call__方法)、lambda函数、生成器都可以作用这个地方的参数。这种灵活性,衍生出众多的应用场景。

标签:02,defaultdict,函数,dic,__,print,常值,defaultdic
From: https://blog.csdn.net/2301_80452984/article/details/136544781

相关文章

  • 2024计算机软考基本介绍、考试时间、考试科目等2024年软考新变化政策 证书的作用
    专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-先导篇)案例分析篇......
  • 第15届蓝桥杯青少组STEMA考试C++中高级真题试卷(2024年3月)编程题部分
    编程题第6题   问答题编程实现:寒假期间小明需要做完n张试卷,但他每天最多能做完m张,请计算出小明做完n张试卷最少需要多少天?输入描述一行输入两个整数n和m(1≤n≤100,1≤m≤10),分别表示要完成的试卷张数,及每天最多能做完的试卷张数,整数之间以一个空格隔开输出描述输出......
  • 联合省选 2024
    以防有人不知道我没进队。Day0润之前打了会PVZSE,好久没打唐了。车站吃KFC。去的路上florr出了upeas,5.8kdmg14.6khealth,这就是u卡的强度吗但是peas总是打不中就很唐,upeas到底咋用啊晚上和int_R玩overcooked,只三星了3关.jpgDay1D1打的不是很唐。晚上......
  • 洛谷题单指南-线性表-P2234 [HNOI2002] 营业额统计
    原题链接:https://www.luogu.com.cn/problem/P2234题意解读:要计算每一天最小波动值的和,需要对每一天求最小波动值,再求和,如果暴力法,时间复杂度在1+2+3+......+32767≈5*10^8,可能会超时。解题思路:1、暴力法:由于本题测试数据比较水,实测暴力求解直接可以AC,由于没有技术含量,不做具体......
  • 02-CSS
    CascadingStyleSheet层叠样式表CSS是一种描述HTML文档样式的语言CSS描述应该如何显示HTML元素CSS的基本原则是内容与样式相分离CSSSelector标签选择器TagSelector作用于所有标签<!DOCTYPEhtml><html> <head> <metacharset="utf-8"> <title>CSS基础:刘......
  • 2024-3-12
    上午睡觉,下午一下午课,大物实验老师跟犯病了似的,不过无所谓,晚上去上了双创课,于迅博老师感冒了,下课了去找他,他要赶校车,上校车给我打电话了解情况,上自己车后又给我打电话,打了46分钟,真是负责任啊。老师之间的差距是真的大,3月3号问张继威的问题现在还没回我呢。谈话内容总结:老师建议读......
  • 华为OD机试真题-模拟目录管理-2024年OD统一考试(C卷)
    题目描述:实现一个模拟目录管理功能的软件,输入一个命令序列,输出最后一条命令运行结果。支持命令: 1)创建目录命令:mkdir目录名称,如mkdirabc为在当前目录创建abc目录,如果已存在同名目录则不执行任何操作。此命令无输出。 2)进入目录命令:cd目录名称,如cdabc为进入abc目录,......
  • 2024基于协同过滤算法springboot微信订餐小程序项目
    项目介绍基于springboot开发的订餐小程序,用户在微信小程序里面进行注册登录,点餐,收藏,评论等,管理员在后台网页端进行对菜品,分类,订单,用户,角色,评论等进行管理,小程序界面通过协同过滤算法给用户推荐菜品技术栈后端:springboot+JPA+Mysql8+redis+maven+idea前端:后台:HTML+JS+CSS......
  • 【快捷部署】002_Flink
    Flink一键安装(本地模式)install-flink.sh脚本内容#!/bin/bash####变量###执行脚本的当前目录mydir=$(cd"$(dirname"$0")";pwd)echo$mydir#flink安装目录flink=/flink#检查点目录cp=$flink/checkpoints/#保留点目录sp=$flink/savepoints/#tasknumber数量ta......
  • <2024最新>ChatGPT逆向教程
    前言在使用本篇文章用到的项目以及工具时,需要对其有一定的了解,无法访问以及无法使用的问题作者不承担任何责任,可以自行想办法解决遇到的问题​。文章若有不合适,有问题的地方,请私聊指出,谢谢~准备工具一台至少2核2G内存的服务器,推荐是位于香港、新加坡或日本地区的服......