百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

清华大佬告诉你如何使用Python:高效操作文件的三个建议

off999 2024-10-18 08:05 25 浏览 0 评论

前言

在这个世界上,人们每天都在用 Python 完成着不同的工作。而文件操作,则是大家最常需要解决的任务之一。使用 Python,你可以轻松为他人生成精美的报表,也可以用短短几行代码快速解析、整理上万份数据文件。

当我们编写与文件相关的代码时,通常会关注这些事情:我的代码是不是足够快?我的代码有没有事半功倍的完成任务? 在这篇文章中,我会与你分享与之相关的几个编程建议。我会向你推荐一个被低估的 Python 标准库模块、演示一个读取大文件的最佳方式、最后再分享我对函数设计的一点思考。

如下图回复01获得学习Python zi 廖!

下面,让我们进入第一个“模块安利”时间吧。

注意:因为不同操作系统的文件系统大不相同,本文的主要编写环境为 Mac OS/Linux 系统,其中一些代码可能并不适用于 Windows 系统。

建议一:使用 pathlib 模块

如果你需要在 Python 里进行文件处理,那么标准库中的 os 和 os.path 兄弟俩一定是你无法避开的两个模块。在这两个模块里,有着非常多与文件路径处理、文件读写、文件状态查看相关的工具函数。

让我用一个例子来展示一下它们的使用场景。有一个目录里装了很多数据文件,但是它们的后缀名并不统一,既有 .txt,又有 .csv。我们需要把其中以 .txt 结尾的文件都修改为 .csv 后缀名。

我们可以写出这样一个函数:

让我们看看,上面的代码一共用到了哪些与文件处理相关的函数:

os.listdir(path):列出 path 目录下的所有文件(含文件夹)

os.path.splitext(filename):切分文件名里面的基础名称和后缀部分

os.path.join(path,filename):组合需要操作的文件名为绝对路径

os.rename(...):重命名某个文件

上面的函数虽然可以完成需求,但说句实话,即使在写了很多年 Python 代码后,我依然觉得:这些函数不光很难记,而且最终的成品代码也不怎么讨人喜欢。

使用 pathlib 模块改写代码

为了让文件处理变得更简单,Python 在 3.4 版本引入了一个新的标准库模块:pathlib。它基于面向对象思想设计,封装了非常多与文件操作相关的功能。如果使用它来改写上面的代码,结果会大不相同。

使用 pathlib 模块后的代码:

和旧代码相比,新函数只需要两行代码就完成了工作。而这两行代码主要做了这么几件事:

1.首先使用 Path(path) 将字符串路径转换为 Path 对象

2.调用 .glob('*.txt') 对路径下所有内容进行模式匹配并以生成器方式返回,结果仍然是 Path 对象,所以我们可以接着做后面的操作

3.使用 .with_suffix('.csv') 直接获取使用新后缀名的文件全路径

4.调用 .rename(target) 完成重命名

相比 os 和 os.path,引入 pathlib 模块后的代码明显更精简,也更有整体统一感。所有文件相关的操作都是一站式完成。

其他用法

除此之外,pathlib 模块还提供了很多有趣的用法。比如使用 / 运算符来组合文件路径:

或者使用 .read_text() 来快速读取文件内容:

除了我在文章里介绍的这些,pathlib 模块还提供了非常多有用的方法,强烈建议去 官方文档 详细了解一下。

如果上面这些都不足以让你动心,那么我再多给你一个使用 pathlib 的理由:PEP-519 里定义了一个专门用于“文件路径”的新对象协议,这意味着从该 PEP 生效后的 Python 3.6 版本起,pathlib 里的 Path 对象,可以和以前绝大多数只接受字符串路径的标准库函数兼容使用:

所以,无需犹豫,赶紧把 pathlib 模块用起来吧。

Hint: 如果你使用的是更早的 Python 版本,可以尝试安装 pathlib2 模块 。

建议二:掌握如何流式读取大文件

几乎所有人都知道,在 Python 里读取文件有一种“标准做法”:首先使用 withopen(fine_name) 上下文管理器的方式获得一个文件对象,然后使用 for 循环迭代它,逐行获取文件里的内容。

下面是一个使用这种“标准做法”的简单示例函数:

假如我们有一个文件 small_file.txt,那么使用这个函数可以轻松计算出 9 的数量。

为什么这种文件读取方式会成为标准?这是因为它有两个好处:

1.with 上下文管理器会自动关闭打开的文件描述符

2.在迭代文件对象时,内容是一行一行返回的,不会占用太多内存

标准做法的缺点

但这套标准做法并非没有缺点。如果被读取的文件里,根本就没有任何换行符,那么上面的第二个好处就不成立了。当代码执行到 forlineinfile 时,line 将会变成一个非常巨大的字符串对象,消耗掉非常可观的内存。

让我们来做个试验:有一个 5GB 大的文件 big_file.txt,它里面装满了和 small_file.txt 一样的随机字符串。只不过它存储内容的方式稍有不同,所有的文本都被放在了同一行里:

如果我们继续使用前面的 count_nine 函数去统计这个大文件里 9 的个数。那么在我的笔记本上,这个过程会足足花掉 65 秒,并在执行过程中吃掉机器 2GB 内存 [注1]。

使用 read 方法分块读取

为了解决这个问题,我们需要暂时把这个“标准做法”放到一边,使用更底层的 file.read() 方法。与直接循环迭代文件对象不同,每次调用 file.read(chunk_size) 会直接返回从当前位置往后读取 chunk_size 大小的文件内容,不必等待任何换行符出现。

所以,如果使用 file.read() 方法,我们的函数可以改写成这样:

在新函数中,我们使用了一个 while 循环来读取文件内容,每次最多读取 8kb 大小,这样可以避免之前需要拼接一个巨大字符串的过程,把内存占用降低非常多。

利用生成器解耦代码

假如我们在讨论的不是 Python,而是其他编程语言。那么可以说上面的代码已经很好了。但是如果你认真分析一下 count_nine_v2 函数,你会发现在循环体内部,存在着两个独立的逻辑:数据生成(read 调用与 chunk 判断)数据消费。而这两个独立逻辑被耦合在了一起。

正如我在《编写地道循环》这篇文章里所提到的,为了提升复用能力,我们可以定义一个新的 chunked_file_reader 生成器函数,由它来负责所有与“数据生成”相关的逻辑。这样 count_nine_v3里面的主循环就只需要负责计数即可。

进行到这一步,代码似乎已经没有优化的空间了,但其实不然。iter(iterable) 是一个用来构造迭代器的内建函数,但它还有一个更少人知道的用法。当我们使用 iter(callable,sentinel) 的方式调用它时,会返回一个特殊的对象,迭代它将不断产生可调用对象 callable 的调用结果,直到结果为 setinel 时,迭代终止。

最终,只需要两行代码,我们就完成了一个可复用的分块文件读取函数。那么,这个函数在性能方面的表现如何呢?

和一开始的 2GB 内存/耗时 65 秒 相比,使用生成器的版本只需要 7MB 内存 / 12 秒 就能完成计算。效率提升了接近 4 倍,内存占用更是不到原来的 1%。

建议三:设计接受文件对象的函数

统计完文件里的 “9” 之后,让我们换一个需求。现在,我想要统计每个文件里出现了多少个英文元音字母(aeiou)。只要对之前的代码稍作调整,很快就可以写出新函数 count_vowels。

和之前“统计 9”的函数相比,新函数变得稍微复杂了一些。为了保证程序的正确性,我需要为它写一些单元测试。但当我准备写测试时,却发现这件事情非常麻烦,主要问题点如下:

函数接收文件路径作为参数,所以我们需要传递一个实际存在的文件为了准备测试用例,我要么提供几个样板文件,要么写一些临时文件而文件是否能被正常打开、读取,也成了我们需要测试的边界情况如果,你发现你的函数难以编写单元测试,那通常意味着你应该改进它的设计。上面的函数应该如何改进呢?

答案是:让函数依赖“文件对象”而不是文件路径。

修改后的函数代码如下:

这个改动带来的主要变化,在于它提升了函数的适用面。因为 Python 是“鸭子类型”的,虽然函数需要接受文件对象,但其实我们可以把任何实现了文件协议的 “类文件对象(file-like object)” 传入 count_vowels_v2 函数中。

而 Python 中有着非常多“类文件对象”。比如 io 模块内的 StringIO 对象就是其中之一。它是一种基于内存的特殊对象,拥有和文件对象几乎一致的接口设计。

利用 StringIO,我们可以非常方便的为函数编写单元测试。

使用 pytest 运行测试可以发现,函数可以通过所有的用例:

而让编写单元测试变得更简单,并非修改函数依赖后的唯一好处。除了 StringIO 外,subprocess 模块调用系统命令时用来存储标准输出的 PIPE 对象,也是一种“类文件对象”。这意味着我们可以直接把某个命令的输出传递给 count_vowels_v2 函数来计算元音字母数:

正如之前所说,将函数参数修改为“文件对象”,最大的好处是提高了函数的 适用面 和 可组合性。通过依赖更为抽象的“类文件对象”而非文件路径,给函数的使用方式开启了更多可能,StringIO、PIPE 以及任何其他满足协议的对象都可以成为函数的客户。

不过,这样的改造并非毫无缺点,它也会给调用方带来一些不便。假如调用方就是想要使用文件路径,那么就必须得自行处理文件的打开操作。

如何编写兼容二者的函数

有没有办法即拥有“接受文件对象”的灵活性,又能让传递文件路径的调用方更方便?

答案是:有,而且标准库中就有这样的例子。

打开标准库里的 xml.etree.ElementTree 模块,翻开里面的 ElementTree.parse 方法。你会发现这个方法即可以使用文件对象调用,也接受字符串的文件路径。而它实现这一点的手法也非常简单易懂:

使用这种基于“鸭子类型”的灵活检测方式, count_vowels_v2 函数也同样可以被改造得更方便,我在这里就不再重复啦。总结

文件操作我们在日常工作中经常需要接触的领域,使用更方便的模块、利用生成器节约内存以及编写适用面更广的函数,可以让我们编写出更高效的代码。

1.使用 pathlib 模块可以简化文件和目录相关的操作,并让代码更直观

2.PEP-519 定义了表示“文件路径”的标准协议,Path 对象实现了这个协议

3.通过定义生成器函数来分块读取大文件可以节约内存

4.使用 iter(callable,sentinel) 可以在一些特定场景简化代码

5.难以编写测试的代码,通常也是需要改进的代码

6.让函数依赖“类文件对象”可以提升函数的适用面和可组合性

注解

视机器空闲内存的多少,这个过程可能会消耗比 2GB 更多的内存。

最后

小编近几年在学习Python!对于想学习Python的朋友们,我想说:很多人学了一个星期就放弃了,为什么呢?其实没有好的学习资料给你去学习,你们是很难坚持的,这是小编收集的Python入门学习资料!

相关推荐

手写输入法手写板怎么设置(手写输入法手写板怎么设置 iphone)

手写输入法设置方法:1、打开手机至桌面主页,保持手机处于工作状态;  2、在手机桌面主页列表找到设置,点击打开并进入设置主界面;  3、在设置主界面找到系统设置选项,点击打开并进入系统设置主界面;  ...

推荐杀毒软件(下载杀毒软件最新版)
推荐杀毒软件(下载杀毒软件最新版)

好的杀毒软件有360杀毒,金山毒霸,AVG杀毒等等。1,360杀毒,品牌介绍:360安全卫士品牌是一款由奇虎360公司推出的功能强、效果好、受用户欢迎的安全杀毒软件。360安全卫士拥有查杀木马、清理插件、修复漏洞、电脑体检、电脑救援、保护隐...

2025-11-12 08:51 off999

百度云盘官网(百度云盘官网下载)

网盘在手机上面仅仅是一个图标,他是不占用手机硬盘的,当然,如果你从网盘上下载了文件到本地,下载的文件就会占用手机的硬盘空间。要找到这个文件,可以通过文件管理器来进行查找,也可以通过路径的方式来查找,但...

路由器设置管理系统(路由器设置管理员密码怎么设置)
路由器设置管理系统(路由器设置管理员密码怎么设置)

电脑端:把华为路由器接通电源,并开启WIFI,如果你使用电脑PC端,请使用网线连接路由器;成功连接到路由器之后,打开电脑浏览器,在浏览器中输入IP地址:192.168.3.1即可进入华为路由器管理界面;进入我要上网页面。在上网方式下拉框中,...

2025-11-12 07:51 off999

win10家庭版密钥在哪里看(windows10家庭版密钥在哪里)

要查看Windows10系统的激活密钥,请按照以下步骤操作: 1.点击“开始”菜单,然后选择“设置”(齿轮图标)。 2.在“设置”窗口中,选择“更新和安全”。 ...

两个wifi路由器怎么连接(两个wifi路由器设置方法)

无线路由器再连接一个无线路由器的设置方法如下:一、主路由器设置方法:先将网线(总线)插在无线路由器的WAN口,用一条网线一头插入无线路由器的任意LAN口一头连接电脑网卡,开启路由器的电源。电脑打开浏览...

windows7的安装过程(windows7安装步骤有哪些)

安装教程注意事项:1、本系统可以直接安装,不需要制作U盘启动盘,适合能正常开机的电脑。2、安装系统后C盘(包括桌面)数据会丢失,重要的东西请自行备份。3、此安装方法只适用于当前系统为XP、Win7、W...

惠普打印机插件手机版(惠普打印插件app)

您好,感谢您选择惠普产品。不是所有的打印机都可以实现手机打印功能的。一般来说支持ariprint的打印机,并且手机也有该功能,才能实现打印功能。如果您不需要实现打印功能,那么这个插件对您来说可能用处不...

电脑热点怎么禁止别人连接(怎么让电脑的热点不被关闭)

方法/步骤1/7首先,在我们的屏幕上找到“设置”。2/7点击进入设置之后,正数第三位就是个人热点。3/7在个人热点中,我们可以设置热点密码,开启关闭热点。4/7排名第三的是连接管理,可以设置允许一个至...

常用浏览器有哪些

电脑常用浏览器好用的方法步骤1从用户体验和功能扩展性来看,Chrome是最好用的浏览器。2Chrome拥有快速的页面加载速度、优秀的标签页管理和内置的开发者工具等功能,还有不断更新的扩展程序,能够...

u盘怎么取消读写保护(优盘怎么去掉读写保护)

如果您的U盘启动了读写保护,那么就无法进行数据的读取和写入操作。以下是一些可能的解决方法:1.检查开关或按钮:一些U盘有物理开关或按钮,用于启用或禁用读写保护。您可以检查一下U盘上是否有这样的开关或...

打印机脱机无法打印怎么办(打印机脱机无法打印故障处理)
打印机脱机无法打印怎么办(打印机脱机无法打印故障处理)

打印机脱机无法打印怎么办?在使用打印机的过程中,经常会遇到打印机无法打印的问题,如果你的打印机已经正常使用了一段时间,而是现在打印机无法打印了,那么很可能是你的打印机脱机了。我们该怎么办呢?首先我们拿到打印机,要把它的电源线,USB打印线与...

2025-11-12 03:51 off999

台式电脑可以连接wifi吗(台式电脑没有连接wifi选项怎么办)
  • 台式电脑可以连接wifi吗(台式电脑没有连接wifi选项怎么办)
  • 台式电脑可以连接wifi吗(台式电脑没有连接wifi选项怎么办)
  • 台式电脑可以连接wifi吗(台式电脑没有连接wifi选项怎么办)
  • 台式电脑可以连接wifi吗(台式电脑没有连接wifi选项怎么办)
激活码怎么激活(激活码怎么激活steam)

首先,启动电脑,在键盘按下“Win+R”,然后“运行”程序。然后,在“运行”的对话框输入“regedit”,回车确定输入命令然后,在窗口的左侧菜单选择“HKEY_LOCAL_MACHINE\SOFTW...

pscs6安装教程序列号(ps安装序列号cs6破解)
  • pscs6安装教程序列号(ps安装序列号cs6破解)
  • pscs6安装教程序列号(ps安装序列号cs6破解)
  • pscs6安装教程序列号(ps安装序列号cs6破解)
  • pscs6安装教程序列号(ps安装序列号cs6破解)

取消回复欢迎 发表评论: