我们在采用 dict 的时候,一般都需要判断键是否存在,如果不存在,设置一个默认值,存在则采取其他的操作,但这个做法其实需要多写几行代码,那么是否有更高效的写法,可以减少代码,但可读性又不会降低呢,毕竟作为程序员,我们都希望写出可用并且高效简洁的代码。

今天看到一篇文章,作者介绍可以使用 defaultdictCounter 来代替 dictionary 可以写出比更加简洁和可读性高的代码,因此今天就简单翻译这篇文章,并后续简单介绍这两种数据类型。

原文:

https://towardsdatascience.com/python-pro-tip-start-using-python-defaultdict-and-counter-in-place-of-dictionary-d1922513f747

关于字典的介绍,也可以查看我之前写的Python基础入门_2基础语法和变量类型。

本文目录:

  • Counter 和 defaultdict
  • 为何要用 defaultdict 呢?
  • defaultdict 的定义和使用
  • Counter 的定义和使用

学习一门编程语言很简单,在学习一门新语言的时候,我会专注于以下顺序的知识点,并且开始用新语言写代码其实很简单:

  • 运算符和数据类型:+,-,int,float,str
  • 条件语句:if,else,case,switch
  • 循环语句:For,while
  • 数据结构:List,Array,Dict,Hashmaps
  • 定义函数

但能写代码和写出优雅高效的代码是两件事情,每种语言都有其独特的地方。

因此,一门编程语言的新手总是会写出比较过度的代码,比如,对于 Java 开发者,在学习 Python 后,要写一段实现对一组数字的求和代码,会是下面这样子:

x=[1,2,3,4,5]
sum_x = 0
for i in range(len(x)):sum_x+=x[i]

但对于一名 Python 老手来说,他的代码是:

sum_x = sum(x)

所以接下来会开启一个名为“Python Shorts”的系列文章,主要介绍一些 Python 提供的简单概念以及有用的技巧和使用例子,这个系列的目标就是写出高效并且可读的代码。

Counter 和 defaultdict

这里的要代码实现的例子就是统计一段文字中单词出现的次数,比如《哈姆雷特》,应该如何做呢?

Python 中可以有多种实现方法,但只有一种是比较优雅的,那就是采用原生 Python 的实现–dict 数据类型。

代码如下所示:

# count the number of word occurrences in a piece of text
text = "I need to count the number of word occurrences in a piece of text. How could I do that? " \"Python provides us with multiple ways to do the same thing. But only one way I find beautiful."word_count_dict = {}
for w in text.split(" "):if w in word_count_dict:word_count_dict[w] += 1else:word_count_dict[w] = 1

这里还可以应用 defaultdict 来减少代码行数:

from collections import defaultdict
word_count_dict = defaultdict(int)
for w in text.split(" "):word_count_dict[w] += 1

利用 Counter 也可以做到:

from collections import Counter
word_count_dict = Counter()
for w in text.split(" "):word_count_dict[w] += 1

Counter 还有另一种写法,更加简洁:

word_counter = Counter(text.split(" "))

Counter 其实就是一个计数器,它本身就应用于统计给定的变量对象的次数,因此,我们还可以获取出现次数最多的单词:

print('most common word: ', word_count_dict.most_common(10))

输出如下:

most common word:  [('I', 3), ('the', 2), ('of', 2), ('do', 2), ('to', 2), ('multiple', 1), ('in', 1), ('way', 1), ('us', 1), ('occurrences', 1)]

其他的一些应用例子:

# Count Characters
print(Counter('abccccccddddd'))
# Count List elements
print(Counter([1, 2, 3, 4, 5, 1, 2]))

输出结果:

Counter({'c': 6, 'd': 5, 'a': 1, 'b': 1})
Counter({1: 2, 2: 2, 3: 1, 4: 1, 5: 1})

为何要用 defaultdict 呢?

既然 Counter 这么好用,是不是只需要 Counter 就可以了?答案当然是否定的,因为 Counter 的问题就是其数值必须是整数,本身就是用于统计数量,因此如果我们需要的数值是字符串,列表或者元组,那么就不能继续用它。

这个时候,defaultdict 就派上用场了。它相比于 dict 的最大区别就是可以设置默认的数值,即便 key 不存在。例子如下:

s = [('color', 'blue'), ('color', 'orange'), ('color', 'yellow'), ('fruit', 'banana'), ('fruit', 'orange'),('fruit', 'banana')]
d = defaultdict(list)
for k, v in s:d[k].append(v)
print(d)

输出结果:

defaultdict(<class 'list'>, {'color': ['blue', 'orange', 'yellow'], 'fruit': ['banana', 'orange', 'banana']})

这里就是事先将字典的所有数值都初始化一个空列表,而如果是传入集合 set

s = [('color', 'blue'), ('color', 'orange'), ('color', 'yellow'), ('fruit', 'banana'), ('fruit', 'orange'),('fruit', 'banana')]
d = defaultdict(set)
for k, v in s:d[k].add(v)
print(d)

输出结果:

defaultdict(<class 'set'>, {'color': {'blue', 'yellow', 'orange'}, 'fruit': {'banana', 'orange'}})

这里需要注意的就是列表和集合的添加元素方法不相同,列表是list.append(),而集合是set.add()


接着是补充下,这两个数据类型的一些定义和方法,主要是参考官方文档的解释。

defaultdict 的定义和使用

关于 defaultdict,在官方文档的介绍有:

class collections.defaultdict([default_factory[, …]])

返回一个新的类似字典的对象。 defaultdict 是内置 dict 类的子类。它重载了一个方法并添加了一个可写的实例变量。其余的功能与 dict 类相同,此处不再重复说明。

第一个参数 default_factory 提供了一个初始值。它默认为 None 。所有的其他参数都等同与 dict 构建器中的参数对待,包括关键词参数。

dict 有一个方法setdefault(),利用它也可以实现比较简洁的代码:

s = [('color', 'blue'), ('color', 'orange'), ('color', 'yellow'), ('fruit', 'banana'), ('fruit', 'orange'),('fruit', 'banana')]
a = dict()
for k, v in s:a.setdefault(k, []).append(v)
print(a)

但官方文档也说了,defaultdict 的实现要比这种方法更加快速和简单。

Counter 的定义和使用

中文官方文档的说明:

class collections.Counter([iterable-or-mapping])

一个 Counter 是一个 dict 的子类,用于计数可哈希对象。它是一个集合,元素像字典键(key)一样存储,它们的计数存储为值。计数可以是任何整数值,包括0和负数。 Counter 类有点像其他语言中的 bags或multisets。

这里,应该不只是可哈希对象,还有可迭代对象,否则列表属于不可哈希对象,是否可哈希,其实是看该数据类型是否实现了 __hash__ 方法:

a = (2, 1)
a.__hash__()

输出:

3713082714465905806

而列表:

b=[1,2]
b.__hash__()

报错:

TypeError: 'NoneType' object is not callable

当然,之前也提过,调用hash() 方法,也同样可以判断一个数据类型是否可哈希,而可哈希的数据类型都是不可变的数据类型。

对于 Counter ,还可以通过关键字来初始化:

c = Counter(cats=4, dogs=8)
print(c)

输出:

Counter({'dogs': 8, 'cats': 4})

Counter 的一些方法,除了上述介绍的most_common()外,还有:

  • elements():返回一个迭代器,将所有出现元素按照其次数来重复 n 个,并且返回任意顺序,但如果该元素统计的次数少于 1 ,则会忽略,例子如下:
c = Counter(a=4, b=2, c=0, d=-2)
sorted(c.elements())
# ['a', 'a', 'a', 'a', 'b', 'b']
  • subtract():减法操作,输入输出可以是 0 或者负数
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
print(c)
# Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

此外,还有以下这些方法:

# 求和
sum(c.values())
# 清空 Counter
c.clear()
# 转换为 列表
list(c)
# 转换为 集合
set(c)
# 转换为 字典
dict(c)
# 键值对
c.items()
#
Counter(dict(list_of_pairs))
# 输出 n 个最少次数的元素
c.most_common()[:-n-1:-1]
# 返回非零正数
+c
# 返回负数
-c

此外,也可以采用运算符+,-,&,|,各有各不同的实现作用:

c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
# 加法操作 c[x] + d[x]
print(c + d)    # Counter({'a': 4, 'b': 3})
# 减法,仅保留正数
print(c - d )   # Counter({'a': 2})
# 交集:  min(c[x], d[x])
print(c & d)    # Counter({'a': 1, 'b': 1})
# 并集:  max(c[x], d[x])
print(c | d)    # Counter({'a': 3, 'b': 2})

参考:

  • collections— 容器数据类型
  • What does “hashable” mean in Python?

小结

如果需要进行计数,比如计算单词出现次数,采用 Counter 是一个不错的选择,非常简洁,可读性也高;而如果需要保存的数据不是整数,并且都是统一的某个类型,比如都是列表,那么直接采用 defaultdict 来定义一个变量对象,会比用 dict 的选择更好。

最后,本文的代码例子已经上传到 Github 上了:

https://github.com/ccc013/Python_Notes/blob/master/Python_tips/defaultdict_and_counter.ipynb

欢迎关注我的微信公众号–算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!

[Python技巧]是时候用 defaultdict 和 Counter 代替 dictionary 了相关推荐

  1. python技巧(setdefault 与 defaultdict)

    转自:https://mp.weixin.qq.com/s/DFF8sRSW7W4nqB6mmLr2jQ 从题目引出 data = [("p", 1), ("p" ...

  2. 你需要知道的20个常用的Python技巧

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|机器学习算法那些事 Python的可读性和简单性是其广受 ...

  3. python%20语言 20培训_Python语言学习之20个值得学习的Python技巧

    点击蓝字获取更多精彩信息 1.字符串反转 使用切片反转字符串. str1="qwert"   rev_str1=str1[::-1]   #输出    # trewq 2.使首字母 ...

  4. python大型项目经验_经验丰富程序员才知道的8种高级Python技巧

    全文共2330字,预计学习时长11分钟 图源:unsplash 本文将介绍8个简洁的Python技巧,若非经验十足的程序员,你肯定有些从未见过.向着更简洁更高效,出发吧! 1.通过多个键值将对象进行排 ...

  5. 你与数据科学家只差这26条python技巧

    戳蓝字"CSDN云计算"关注我们哦! 作者 | Peter Gleeson 来源 | Python数据科学 编译 | wLsq Python是目前世界上最流行的编程语言之一.因为: ...

  6. 资深程序员才知道的30个Python技巧

    Python中的省略号 Python省略号是三点序列,通常在自然语言中使用.但是您可能不知道的是,这也是Python中的有效对象: - Ellipsis 它主要用于NumPy中的矩阵切片操作.但是,您 ...

  7. 一系列有用的Python技巧

    Python作为世界上最受欢迎的编程语言之一.这有很多原因: 易于学习 超级通用 具有大量的模块和库 本文将尝试以A-Z的顺序分享其中一些有用的技巧和窍门.这些"窍门"大部分是我在 ...

  8. 程序员必知的20个Python技巧

    作者 | Duomly 译者 | 弯月,编辑 | 郭芮 出品 | CSDN(ID:CSDNnews) Python是一门流行且应用广泛的通用编程语言,其应用包括数据科学.机器学习.科学计算等领域,以及 ...

  9. 【python技巧】“”、“”等符号操作

    [python技巧] 1)[关于'&' :按照二进制进行与操作,可以用作判断奇偶][https://zhidao.baidu.com/question/308587899.html] & ...

最新文章

  1. php 动态链接,LevelDB(动态链接) + PHP扩展 安装
  2. c++ 一日一练:istream.rdbuf(ostringstream.rdbuf())无效!
  3. mysql 前后代表什么_MySQL的锁
  4. redis-集群分片
  5. 宽屏图片轮播html,jQuery实现宽屏图片轮播实例教程
  6. kmeans 是Nondeterministic algorithm
  7. 20135337——信息安全设计基础第八周学习笔记
  8. web报表工具FineReport的公式编辑框的语法简介
  9. 腾讯推出产业版地图WeMap,“一张图”破局五大行业场景
  10. Java删除ArrayList中的重复元素的2种方法
  11. jvm垃圾收集器与内存分配策略
  12. ifpc币_劳力士手表价格表一览表
  13. ARM汇编书写格式及符号(symbol)和表达式(expr)
  14. 安卓学习征文 -- 自己定义标题栏
  15. 基于python的简易局域网聊天工具
  16. java全栈开发主要工作内容,java全栈工程师
  17. C#桌面办公应用-工资管理系统系列二
  18. 重庆服务器机房维修,重庆市服务器机房ups电源10KVA诚信互利
  19. 请告诉孩子,为什么必须要上大学!
  20. bat脚本通用按键交互控制函数-onkeydown函数

热门文章

  1. 计算机语言乍么设置,电脑如何设置语言
  2. synchronized 异常_由浅入深,Java 并发编程中的 Synchronized
  3. @autowired注解注入为null_Intellij IDEA中Mybatis Mapper自动注入警告的6种解决方案
  4. c语言注释说明被计算机编译系统忽略,C语言程序设计(陈艳 2019-2020-2)-中国大学mooc-题库零氪...
  5. ldd /usr/bin/mysql_mysql客户端登录时报mysql: relocation error错误
  6. linux 复制分区文件,dd复制分区后目标分区的大小变成原分区了
  7. php imap配置,怎么为PHP编译imap扩展?
  8. Linux 网络设备驱动开发(一) —— linux内核网络分层结构
  9. Python 函数装饰器
  10. Windows Sockets 错误码及出错原因