itertools包介绍——可以不用 但不能不知道——python包推荐系列

news2024/11/19 23:33:00

背景1

今天在看一个开源包,发现他的requirements.txt里面放着more_itertools包。

这个包的名字还是挺有意思的,在itertools包前面加上了一个more。难道是python自带的包itertools的加强版?

后来查了一下,这个包,果然是itertools的加强版,好家伙。心里有点害怕😨。

为什么会说,看到这个包,就感到害怕呢。主要是有下面几个方面:

  1. 多:itertools包的函数太多了,实在是记不住,基本上算是我学习python的一大噩梦。
  2. 重要:itertools包是python的函数式编程模块中,相当重要的包。
  3. 难:函数式编程?听起来都很高大上,更别说,用这个包了。

背景2

我其实很喜欢编程,写代码就不带困的。有时候,我会一直研究python的一些官方文档,仔细查看每一个细节。

但是我知道,我看itertools包的文档,真的很困,因为我觉得这个文档太无聊了。

https://docs.python.org/zh-cn/3/library/itertools.html

这里随手截几张itertools的函数列表:



你看每一个函数对于的功能,其实都可以使用他的函数名称来表示。看起来好像不难,但是如果真的是要看懂,好像确实是挺难的。

背景3

既然这个包在python的函数式编程里面的角色这么重要,而且官方文档阅读起来也很费力。那我去网上找一找别人写的案例呗。

但是在网上找了一圈,好像也没找到什么有意思的文章,基本上都在照搬官网的文档,感觉好像没啥意思,感觉写的比python官网还无聊。

进入正题

  1. 想一想,自己学python真的是只是去学习几个牛逼的函数,然后拿出去炫耀,写在项目里面嘛?
  2. 想一想,自己学习编程中,学到的是那些包的优秀用法,还是说学习那些包所蕴含的优秀的编程思想?
  3. 想一想,自己在学习做项目的时候,所谓的成长,是只是把一个项目完成了,还是说掌握了解决一类问题的方法?

其实,我觉得,在有扎实的编程能力之后,我们更需要提升的是解决问题的能力,而思路,在其中发挥着一个相当大的作用。

在本文中,我将从一些现实中的问题出发,分享使用more_itertools包(或者itertools包)解决这些问题的思路。

希望这些思路,可以开阔你编程的思路;

希望这些智慧的结晶,可以抚慰你那被996压榨的灵魂。

案例

这里要说明一下:

  1. itertoolsmore_itertools是两个包。
  2. itertools是python自带的,不需要安装了。
  3. more_itertools需要安装,安装方式为pip install more-itertools。但是在导入的时候,是这样的import more_itertools。希望不要搞混淆了。

1. 按条件分割

民间自古有句话,说的非常好:“什么客,什么菜;什么人,什么待”。

有一天,你的领导对你说:“良睦路程序员,马上要过春节了,给一些客户发点礼物吧,但是不是所有的客户都要发哦,在白名单里面的人,给我发,没在白名单里面的。就不要发了。”

这个时候,其实使用more_itertools包的partition函数就非常方便。

partition函数基本是这个样子的:
不满足条件的对象们, 满足条件的对象们 = partition(条件, 可迭代对象)。基本解释如下:

  1. 条件:是我们写的判断条件。
  2. 可迭代对象:里面有很多元素。会用上面的条件对这里面的每一个元素做判断。
  3. 如果元素不满足条件,就把这个元素放在不满足条件的对象们里面。反之,放在满足条件的对象们里面。

举一个简单的例子:

from more_itertools import partition
res1, res2 = partition(lambda x: x >2, [1,4,3,2,6])
list(res1), list(res2)
# > ([1, 2], [4, 3, 6])

可以看出来:

  1. 不满足条件的,都跑到res1里面了。
  2. 满足条件的,都跑到res2里面了。

那么,我们回到上面领导提出的要求,说白了,就是把

  1. 不在白名单里面的人名字提取出来。
  2. 把白名单里面的人联系方式提取出来(比如邮箱)。

那么使用partition函数就可以这么写:

from typing import Dict, Iterable, Set, Tuple

from more_itertools import partition


def process(
    names: Iterable[str], whitelisted_names: Set[str], name_to_email: Dict[str, str]
) -> Tuple[Iterable[str], Iterable[str]]:
    refused_names, approved_names = partition(
        lambda name: name in whitelisted_names, names
    )  # 1
    approved_emails = {name_to_email[name] for name in approved_names}  # 2
    return refused_names, approved_emails  # 3


def test_only_return_emails_for_approved_users():
    name_to_email = {"John": "john.doe@gmail.com",
                     "Bob": "is.still.using@hotmail.com"}
    names = {"Alice", "Bernard", "Bill"} | set(name_to_email.keys())

    _, actual_emails = process(
        names=names,
        whitelisted_names=set(name_to_email.keys()),
        name_to_email=name_to_email,
    )

    assert set(actual_emails) == set(name_to_email.values())  # 4


def test_filters_non_approved_users():
    not_in_whitelist = {"Alice", "Bernard", "Bill"}
    name_to_email = {"John": "john.doe@gmail.com",
                     "Bob": "is.still.using@hotmail.com"}
    names = not_in_whitelist | set(name_to_email.keys())

    refused_names, _ = process(
        names=names,
        whitelisted_names=set(name_to_email.keys()),
        name_to_email=name_to_email,
    )

    assert set(refused_names) == not_in_whitelist  # 5


在上面的代码中:

  1. #1部分,我们设置了一个条件:判断这个人的名字,在不在白名单里面。如果在白名单里面,就把这些名字放在approved_names里面,如果不在白名单里面,就把名字放在refused_names里面。
  2. #2部分,我们顺着这些满足条件的名字,提取他们对应的联系方式(这里为邮箱)。
  3. #3部分,我们返回不在白名单里面的人的姓名在白名单里面的人的邮箱
  4. #4部分,我们校验:提取的邮箱是不是真的和白名单的邮箱是一摸一样的。
  5. #5部分,我们校验:那些不在白名单里面的人的姓名是不是确实是不在白名单里面的人。

注意上面代码里面加了type hints,就算是不运行代码,也都能了解代码的运行规则(就像是读一篇文章一样)。

实际上,上面的process函数,写法有很多,又不是非要使用partition函数。比如if-else列表推导式。比如下面会提到的map_except函数都可以实现,选择你自己喜欢的即可。

2. 把多层嵌套展开

看我文章的人,大部分都还是20~30岁左右的年轻人吧。大家的人生可能都是这样的:

  1. 小学的时候:玩花片、玩玻璃珠、写作业、吃饭、睡觉。
  2. 初中的时候:去网吧、看小说、听歌、写作业、吃饭、睡觉。
  3. 高中的时候:去网吧、看视频、听歌、写作业、吃饭、睡觉。
  4. 大学的时候:谈恋爱、看视频、听歌、写作业、吃饭、睡觉。
  5. 工作的时候:结婚、买房、买车、逛街、吃饭、睡觉。

大概是这样的,一个图:

my_life = [
    ['小学',['玩花片','玩玻璃珠','写作业','吃饭','睡觉']],
    ['初中',['去网吧','看小说','听歌','写作业','吃饭','睡觉']],
    ['高中',['去网吧','看视频','听歌','写作业','吃饭','睡觉']],
]
# 高中、大学、工作就不列出来了,太麻烦了。

如果这个时候,我们想问自己,我们这一生,到底做了多少事情,经过多少阶段呢?只要把我们的人生,不断的展开,然后去重就行了。一行代码搞定:

from more_itertools import flatten, collapse
set(collapse(my_life))
#> {'写作业', '初中', '去网吧', '吃饭', '听歌', '小学', '玩玻璃珠', '玩花片', '看小说', '看视频', '睡觉', '高中'}

是的,我们要介绍collapse了。

collapseflatten函数功能差不多的,都是相当于把可迭代对象当成洋葱。把他们一层一层剥开,展平。但是有个差异:

  1. flatten函数只能展开一层。就是剥洋葱,剥一层他可以的,剥多了,她就说就辣眼睛了。
  2. collapse函数就很牛逼了。直接剥洋葱剥到内心,辣眼睛?那是不存在的,她就是这么强。

再来一个案例,如果我们希望把字典的每一个值都都提取出来,然后去重,那么常规的代码是这样的:

from typing import Dict, Set


def flatten_multivalues(key_to_values: Dict[str, Set[int]]) -> Set[int]:
    all_values = set()
    for values in key_to_values.values():
        all_values.update(values)
    return all_values


def test_flattens_multivalue_dicts():
    # shamelessly taken from: https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up
    term_to_document_indices = {
        "choice": {3},
        "coming": {1},
        "fury": {2},
        "is": {1, 2, 3},
        "the": {2, 3},
    }

    all_document_indices = flatten_multivalues(term_to_document_indices)

    assert all_document_indices == {1, 2, 3}

但是,如果使用这个flatten函数,就简单多了,大概是这样的:

from typing import Dict, Set
from more_itertools import flatten


def flatten_multivalues(key_to_values: Dict[str, Set[int]]) -> Set[int]:
    return set(flatten(key_to_values.values()))


def test_flattens_multivalue_dicts():
    # shamelessly taken from: https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up
    term_to_document_indices = {
        "choice": {3},
        "coming": {1},
        "fury": {2},
        "is": {1, 2, 3},
        "the": {2, 3},
    }

    all_document_indices = flatten_multivalues(term_to_document_indices)

    assert all_document_indices == {1, 2, 3}

这里插一句,还有人记得numpy函数也有flatten函数吧。

import numpy as np 
data = np.random.randint(0, 10, size=(3,4))

data.flatten()
#> array([3, 9, 9, 5, 5, 7, 7, 3, 9, 2, 6, 4])

3. 犯错误也别怕

在成长的过程中,谁没犯错误过。

  1. 考试的时候,涂错了答题卡。没事儿,只是小的模拟考试。
  2. 工作的时候,不小心造成了一个小错误,损失几个亿。没事儿,反正又不是你的钱。
  3. 恋爱的时候,惹女朋友生气了,没事儿,哄一哄就行了。

就是这样,很多时候,在我们生活中,虽然犯错了,确实没什么大影响的。只要不是致命的错误即可。

就像是下面的代码一样。下面代码做了一个demo:将一串列表转换成int类型的列表。

虽然int('three') => ValueError会报错、虽然int(None) => TypeError会报错。

但是这两个错误,我们都对map_except说了:“如果犯了这两个错误,不要紧,请继续”

from more_itertools import map_except
iterable = ['1', '2', 'three', '4', None]
list(map_except(int, iterable, ValueError, TypeError))
#> [1, 2, 4]

在上面的代码中,可以使用try...except...来忽视一些错误,但是在有些情况下,这么做难免有些麻烦。而上面的map_except函数就可以做到很简单。

这里也把try...except...解决办法分享出来。大家可以对比一下。

from typing import List, Iterable


def process(x: Iterable) -> List[int]:
    def small_func(x) -> int:
        try:
            return int(x)
        except (TypeError, ValueError) as e:
            return None

    res = [small_func(i) for i in x]
    res = [i for i in res if i is not None]
    return res

process([1,2,'three', 4, None])

以前其实我还是很喜欢列表推导式的,可是这样一对比,确实使用map_except更简单。

还记得上面那个给白名单客户发邮箱的故事么?使用map_except同样是可以做到。具体方法就是直接找这个人是不是在白名单里面。如果在白名单里面,就返回邮箱,如果不在白名单里面。虽然会爆出keyError(因为白名单是一个dict数据结构,找不到key,就会爆出keyError错误),但是这个错误是在我们运行的情况下。因此没事。这里也分享出代码:

from typing import Dict, Iterable, Set, Tuple

from more_itertools import map_except


def process(
    names: Iterable[str], whitelisted_names: Set[str], name_to_email: Dict[str, str]
) -> Iterable[str]:
    whitelisted_name_to_email = {
        name: email for name, email in name_to_email.items() if name in whitelisted_names
    }  # 1
    return map_except(
        lambda name: whitelisted_name_to_email[name],  # 3
        names,  # 2
        KeyError  # 4
    )


def test_only_return_emails_for_approved_users():
    name_to_email = {"John": "john.doe@gmail.com", "Bob": "is.still.using@hotmail.com"}
    names = {"Alice", "Bernard", "Bill"} | set(name_to_email.keys())

    actual_emails = process(
        names=names,
        whitelisted_names=set(name_to_email.keys()),
        name_to_email=name_to_email,
    )

    assert set(actual_emails) == set(name_to_email.values())

4. 分割

  1. 这个季节,很适合吃甘蔗。我觉得没人吃甘蔗,是拿着一整根吃吧。当然是要切开,分段吃。
  2. 还记得pandas在处理大数据的时候,他有一个chunk_size,这样在处理的时候,就算是内存不够,也可以分批处理数据。
  3. 很多人可能也都是搞过pytorch、tensorflow训练nlp、cv模型。加载数据的时候,也都是一个batch进去,然后不断的迭代更新。

其实有的时候,我们也需要把列表切开,最常见的就是把一段长的list文本,按照指定的大小,分段,然后放到模型里面,进行encoding。在以前,可能就是会这么写:


liststr = [f'text_{i}' for i in range(11)]
batch_size = 3
[liststr[i:(i+batch_size)] for i in range(0,len(liststr),batch_size)] #1

#> [['text_0', 'text_1', 'text_2'],
#>  ['text_3', 'text_4', 'text_5'],
#>  ['text_6', 'text_7', 'text_8'],
#>  ['text_9', 'text_10']]
  1. #1这段代码其实是很巧妙的,我怀疑大部分人都不知道可以这么用。
  2. 但是#1这段代码,也太长了。不瞒你说,我刚开始也都不会写,写了几次之后,搞懂其中的原理之后,倒是很懂了。
  3. 但是就算是你很熟练,代码写的长,也有很大概率出错。

那么接下来分享一个非常简单的函数,使用这个函数,就可以达到相同的目的。

from more_itertools import chunked

liststr = [f'text_{i}' for i in range(11)]
list(chunked(liststr, 3))

#> [['text_0', 'text_1', 'text_2'],
#>  ['text_3', 'text_4', 'text_5'],
#>  ['text_6', 'text_7', 'text_8'],
#>  ['text_9', 'text_10']]

注意,这里只是分享一下思路,我并不希望你以后都只是用chunked,而不再用上面那个比较长的写法。

更多

  1. 我在做排列组合的时候,我会使用itertools包的product函数、permutations函数、combinations函数。相对于的,numpy里面有np.meshgrid
  2. 我之前在做列表展开的时候,我会使用itertools包的chain函数。

这些函数,我觉得都可以试一试,以至于more_itertools包的官网上的函数,也可以看看。

这里给出若干条我觉得比较好的参考链接。有兴趣完全可以探索一下:

  1. https://more-itertools.readthedocs.io/en/stable/
  2. https://www.gidware.com/real-world-more-itertools/#map_except
  3. https://docs.python.org/zh-cn/3/library/itertools.html
  4. https://martinheinz.dev/blog/16
  5. https://www.bbayles.com/index/decorator_factory

总结

itertoolsmore_itertolls在python编程里面,一直是扮演一个相当重要的角色。

但是很多人觉得里面的函数太多了,看不过来,也用不过来。以至于一直都是敬而远之。

本文介绍了几个小的知识点。面对同一个问题,有几种不同的解法。

我并不会觉得哪一种代码写法在任何情况下都非常的优雅。

优雅的不是那个代码,优雅的是那些代码蕴含的前人的智慧。

在我们未来写代码的道路上

可能也不会用这两个包

更有可能使用的编程语言也不一定会局限于python

但是从中我们学到的知识

我们掌握的智慧

都将伴随我们一生

在面对一些棘手的问题的时候

可能,就是一个想法就帮助了你,解决了问题

ok

以上就是本文的全部内容了

如果觉得有用

欢迎关注、转发公众号【统计学人】

下期再见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/147661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

高并发系统设计的15个锦囊

记得很久之前,去面试过字节跳动。被三面的面试官问了一道场景设计题目:如何设计一个高并发系统。当时我回答得比较粗糙,最近回想起来,所以整理了设计高并发系统的15个锦囊,相信大家看完会有帮助的。 如何理解高并发系统…

【云原生 | 51】Docker三剑客之Docker Compose第二节

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 🏅阿里云ACE认证高级工程师 🏅阿里云开发者社区专…

打卡第11天|力扣20. 有效的括号 、 1047. 删除字符串中的所有相邻重复项 、150. 逆波兰表达式求值 。

今天是打卡第十一天,题目主要是栈结构的运用20. 有效的括号题目链接如下:20. 有效的括号我们挨个遍历字符串,每有一个向左的字符串我们就往栈里面存一个向右的括号,遍历到向右的括号时,如果栈中类型相同就弹出&#xf…

在日本之后,中国也发布新型光刻机,ASML加快对中国出口光刻机

由于美国的影响,ASML对中国出售光刻机一直都在摇摆之中,不过2022年底至少有三家中国芯片企业获得ASML的光刻机,显示出ASML的态度再次发生变化,导致如此结果或许在于中国近期宣布的新型光刻机。一、开辟芯片制造新技术说到绕开ASML…

2022年9月CSP认证题解 如此编码(k进制),何以包邮?(背包问题),吉祥物投票(珂朵莉树、懒标记、并查集)

T1 如此编码 思路 由公式 和前缀乘积定义 得mb1a1b2⋅⋅⋅a1a2⋅⋅⋅an−1bnmb_1a_1\times b_2a_1\times a_2\times\times a_{n-1}\times b_nmb1​a1​b2​⋅⋅⋅a1​a2​⋅⋅⋅an−1​bn​, 上述公式可以提取公共乘项aia_iai​,写成m(bnbn−1⋅⋅⋅…

一文搞懂内存映射原理及使用方法

a. 内存映射原理内存映射即在进程的虚拟地址空间中创建一个映射,分为两种:文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。匿名映射:没有文件支持的内存…

2. 因子(factor)、缺失数据(na)、字符串、时间序列数据

课程视频链接:https://www.bilibili.com/video/BV19x411X7C6?p1 本笔记参照该视频,笔记顺序做了些调整【个人感觉逻辑顺畅】,并删掉一些不重要的内容 系列笔记目录【持续更新】:https://blog.csdn.net/weixin_42214698/category_…

字符的编码与乱码

目录 前言 1 计算机中字符的编码分类 1.1 常见非Unicode编码 1.1.1 ASCII 1.1.2 ISO 8859-1 1.1.3 Windows-1252 1.1.4 GB2312 1.1.5 GBK 1.1.6 GB18030 1.1.7 Big5 1.1.8 编码汇总 1.2、Unicode编码 1.2.1 UTF-32 1.2.2 UTF-16 1.2.3 UTF-8 1.2.4 Unicode编码…

A1032 Sharing

Powered by:NEFU AB-IN Link 文章目录A1032 Sharing题意思路代码A1032 Sharing 题意 To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the s…

2032.1.7 学习总结

1.string与int之间的相互转化问题 &#xff08;1&#xff09;int转string&#xff1a;使用to_string函数 引入头文件: #include<string> 转int&#xff0c;float&#xff0c;double都可以 string to_string (int val); string to_string (long val); string to_string…

Java外卖点餐系统小程序+数据库源码(带本地部署搭建文档)源码免费分享!

亲测Java在线点餐系统小程序数据库源码带本地部署搭建文档 需要源码学习可私信我获取。 小程序外卖扫码点餐为客户提供的是最方便的饮食方式,以快速、便捷的点餐业务送货上门为 -客户服务,这省去了客户很多不必要的时间和麻烦,给商家带来更多利益。同时,小程序外卖扫码点餐可…

STM32从固件库到HAL库

让坚持成为一种热爱&#xff0c;极致成为一种精神。历时10个月&#xff0c;目前我又重新回到了程序员的身份&#xff0c;2023想玩不一样的嵌入式。&#x1f680; 目录前言一、安装STM32CubeMX二、配置自己的HAL库MDK工程三、HAL库与固件库的区别1.句柄2.回调函数3.时钟配置4.HA…

Bulma - 免费开源的纯 CSS 前端 UI 框架,专注于构建移动优先的响应式 web 界面

简单易用的 CSS 框架&#xff0c;虽然只有一个 CSS 文件&#xff0c;但功能很强大&#xff0c;在国外很受开发者欢迎&#xff0c;推荐给大家。 关于 Bulma CSS 框架 Bulma 是一个简单、很容易自定义的 CSS UI 框架&#xff0c;提供了众多预定义好的样式&#xff0c;开发者可以…

宠物吸毛器控制板开发,构建理想人宠共居空间

目前养宠物的人越来越多&#xff0c;猫狗等宠物一直存在着严重的掉毛情况&#xff0c;宠物毛发可能会引起过敏等疾病反应&#xff0c;日常生活中清理工作是一大难题&#xff0c;市面上关于宠物的吸毛产品非常的少&#xff0c;为此沐渥开发了一款宠物吸毛器控制板&#xff0c;适…

奇怪,郭德纲小儿子八岁生日,现场照片发布不久却被删除了

2022年1月5日&#xff0c;对于北京德云社来说&#xff0c;是一个双喜临门好日子&#xff0c;首先德云社相声春晚开始录制&#xff0c;其次郭德纲的儿子郭汾阳&#xff0c;也要过八岁生日了。郭汾阳作为德云社的少班主&#xff0c;是郭德纲老师和董事长王惠所生&#xff0c;老来…

与ChatGPT的聊天:如何使用Python连接Neo4j并创建无向图?

0. 前言 尝试请ChatGPT帮写代码&#xff1a; 但发现ChatGPT可以给出逻辑上正确的程序&#xff0c;一旦具体深入询问&#xff0c;有可能会产生错误。 当然&#xff0c;也可能因为提问的人比较少&#x1f923; 不管怎么说&#xff0c;这篇可以当成是对ChatGPT茶余饭后的谈资&am…

数据库,计算机网络、操作系统刷题笔记28

数据库&#xff0c;计算机网络、操作系统刷题笔记28 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

产品重要更新: GcExcel Java Edition 6.0.2 Crack

重要产品更新&#xff1a; JDK 6 和 7 已经走到了生命的尽头。自此&#xff0c; GcExcel Java Edition将不再支持它们&#xff0c;现在将从最新的v6 版本及更高版本开始以JDK 8为目标。GcExcel Java Edition 6.0.2 Crack by Ω578867473 GcExcel高速 Java Excel 电子表格 API …

Zookeeper简介

引言 在分布式环境下&#xff0c;如果舍弃SpringCloud&#xff0c;使用其他的分布式框架&#xff0c;那么注册心中&#xff0c;配置集中管理&#xff0c;集群管理&#xff0c;分布式锁&#xff0c;分布式任务&#xff0c;队列的管理想单独实现怎么办。 Zookeeper介绍 Zookeeper…

Flink 运行架构

1 Flink 运行时的组件 Flink 运行时架构主要包括四个不同的组件&#xff0c;它们会在运行流处理应用程序时协同工作&#xff1a;作业管理器&#xff08;JobManager&#xff09;、资源管理器&#xff08;ResourceManager&#xff09;、任务管理器&#xff08;TaskManager&#…