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

什么是 Python 中的 __pycache__ 文件夹?

off999 2024-10-12 06:17 47 浏览 0 评论

当您开发一个独立的 Python 脚本时,您可能不会注意到目录结构有什么异常。然而,当项目变得越来越复杂时,您通常会决定将部分功能提取到额外的模块或包中。这时,您可能会发现源文件旁边突然出现了一个 __pycache__ 文件夹,而且似乎是随机出现的:

project/
│
├── mathematics/
│   │
│   ├── __pycache__/
│   │
│   ├── arithmetic/
│   │   ├── __init__.py
│   │   ├── add.py
│   │   └── sub.py
│   │
│   ├── geometry/
│   │   │
│   │   ├── __pycache__/
│   │   │
│   │   ├── __init__.py
│   │   └── shapes.py
│   │
│   └── __init__.py
│
└── calculator.py

请注意,当多个子包相互嵌套时,__pycache__ 文件夹可能存在于项目目录树的不同层次。同时,包含 Python 源文件的其他包或文件夹可能不包含这个神秘的缓存目录。

注意:为了保持工作区的整洁,许多 Python IDE 和代码编辑器在开箱配置时都会隐藏 __pycache__ 文件夹,即使这些文件夹存在于您的文件系统中。

简而言之:它让导入 Python 模块变得更快

尽管 Python 是一种解释型编程语言,但它的解释器并不直接对 Python 代码进行操作,因为那样会非常慢。相反,当您运行一个 Python 脚本或导入一个 Python 模块时,解释器会将您的高级 Python 源代码编译成字节码,字节码是代码的中间二进制表示形式。

这种字节码能让解释器跳过重复的步骤,比如将代码编入抽象语法树并进行解析,以及在每次运行相同程序时验证其正确性。只要底层源代码没有改变,Python 就能重复使用中间表示,并立即准备执行。这样可以节省时间,加快脚本的启动速度。

请记住,虽然从 __pycache__ 加载编译过的字节码会使 Python 模块的导入速度更快,但这并不影响它们的执行速度!

为什么要使用字节码,而不是直接将代码编译成底层机器码?虽然机器码可以在硬件上执行,提供极致的性能,但它的可移植性和生成速度都不如字节码。

机器码是一组能被特定 CPU 架构理解的二进制指令,根据操作系统的不同,被封装成 EXE、ELF 或 Mach-O 等容器格式。相比之下,字节码提供了一个与平台无关的抽象层,编译速度通常更快。

Python 使用本地的 __pycache__ 文件夹来存储项目中导入模块的编译字节码。在随后的运行中,解释器将尝试从这些文件夹中加载模块的预编译版本,前提是它们与相应的源文件是最新的。请注意,只有在代码中导入模块而不是在终端中作为脚本执行时,才会触发这种缓存机制。

除了磁盘上的字节码缓存外,Python 还保留了一个内存中的模块缓存,您可以通过 sys.modules 字典访问它。它确保当您在程序的不同位置多次导入同一个模块时,Python 将使用已经导入的模块,而无需重新加载或重新编译。这两种机制共同作用,减少了导入 Python 模块的开销。
接下来,您将了解在导入模块时,Python 加载缓存字节码比编译源代码快多少。

从缓存加载模块的速度有多快?

缓存发生在幕后,通常不会被注意到,因为 Python 编译字节码的速度非常快。此外,除非您经常运行短时 Python 脚本,否则编译步骤与总执行时间相比仍然微不足道。尽管如此,如果没有缓存,如果您有很多模块并多次导入它们,那么与字节码编译相关的开销就会增加。

要测量缓存模块和未缓存模块的导入时间差,可以在 python 命令中传递 -X importtime 选项,或设置等效的 PYTHONPROFILEIMPORTTIME 环境变量。启用该选项后,Python 将显示一个表格,总结导入每个模块所需的时间,包括一个模块依赖于其他模块时的累计时间。

假设你有一个 calculator.py 脚本,它从本地 arithmetic.py 模块导入并调用了一个实用程序函数:

from arithmetic import add

add(3, 4)

导入的模块只定义了一个函数:

def add(a, b):
    return a + b

如您所见,主脚本将 3 和 4 这两个数字的加法运算委托给了从算术模块导入的 add() 函数。

注意:即使你使用from..import语法,它只是将指定的符号导入到当前的命名空间中,Python将读取并编译整个模块。此外未使用导入也会触发编译。

第一次运行脚本时,Python编译并保存你导入的模块的字节码到一个本地的__pycache__文件夹中。如果这样的文件夹不存在,那么Python会继续之前自动创建一个。现在当您再次执行脚本时,只要您没有更改相关的源代码,Python就应该能找到并加载缓存的字节码。

__pycache__文件夹里面有什么?


__pycache__文件夹中包含了模块的缓存版本,这些版本以.pyc文件的形式存储。.pyc文件是Python字节码的二进制表示形式,它们包含了模块的编译后的形式。



Python何时创建缓存文件夹?


Python在导入模块时会自动检查是否需要创建__pycache__文件夹。如果Python解释器有权限在当前目录下创建文件夹,且该目录下有Python源代码文件,则Python会自动生成__pycache__文件夹并在其中存储相应的缓存版本。


什么操作会使缓存失效?


缓存版本的生成是根据源代码文件的修改时间和内容进行的。如果源代码文件发生了更改,Python将重新生成缓存版本。因此,以下操作将使缓存失效:

  • 修改了源代码文件。
  • 从一个Python版本切换到另一个Python版本。
  • 在不同的操作系统上运行相同的代码。


删除缓存文件夹是否安全?


是的,删除__pycache__文件夹通常是安全的。Python会在需要时自动重新生成缓存版本。删除缓存文件夹可能会导致稍微延迟一点,因为Python需要重新编译源代码以生成新的缓存版本,但不会对代码的正确性产生任何影响。


如何递归删除所有缓存文件夹?


你可以使用操作系统提供的命令或Python的第三方库来递归删除所有的__pycache__文件夹。例如,在Unix/Linux系统上,你可以使用find命令:


find . -type d -name '__pycache__' -exec rm -r {} +

或者

import shutil
import os

def remove_pycache(folder):
    for root, dirs, files in os.walk(folder):
        for d in dirs:
            if d == '__pycache__':
                shutil.rmtree(os.path.join(root, d))

remove_pycache('.')

如何防止Python创建缓存文件夹?


如果你不希望Python创建__pycache__文件夹,你可以在运行Python脚本时设置环境变量PYTHONDONTWRITEBYTECODE为1。


export PYTHONDONTWRITEBYTECODE=1

或者

import sys
sys.dont_write_bytecode = True

如何将缓存存储在集中的文件夹中?


如果你希望将所有缓存文件存储在一个集中的文件夹中,而不是在每个模块所在的目录中创建__pycache__文件夹,你可以设置环境变量PYTHONDONTWRITEBYTECODE为一个特定的目录路径。


export PYTHONDONTWRITEBYTECODE=/path/to/cache/folder


缓存的.pyc文件里面是什么?


缓存的.pyc文件包含了模块的编译后的字节码。这些字节码是由Python编译器生成的,可以直接由Python虚拟机执行。

如何读取和执行缓存的字节码?


你可以使用Python的importlib模块来读取和执行缓存的字节码。以下是一个简单的示例:


import importlib.util

# Load cached bytecode
spec = importlib.util.spec_from_file_location("module_name", "/path/to/module.pyc")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

# Use the module
module.some_function()


字节码能够混淆Python程序吗?


虽然缓存的字节码不是为了隐藏代码而设计的,但它们确实可以使源代码不易被直接阅读。然而,这并不等同于真正的代码混淆或加密。如果你需要确保代码的安全性,最好使用专门的代码混淆工具。


如何反汇编缓存的字节码?

你可以使用Python标准库中的dis模块来反汇编缓存的字节码。以下是一个示例:

import dis

# Disassemble cached bytecode
with open("/path/to/module.pyc", "rb") as f:
    dis.dis(f.read())

结论

__pycache__文件夹是Python中用于存储模块缓存版本的特殊文件夹。它可以提高模块导入的速度,并且在大型项目中尤其有用。虽然删除缓存文件夹通常是安全的,但你也可以通过设置环境变量来控制Python是否生成缓存文件夹,以及它们的存储位置。最后,虽然缓存的字节码可以使源代码不易被直接阅读,但它们并不等同于真正的代码混淆或加密,如果需要确保代码的安全性,最好使用专门的工具。

相关推荐

面试官:来,讲一下枚举类型在开发时中实际应用场景!

一.基本介绍枚举是JDK1.5新增的数据类型,使用枚举我们可以很好的描述一些特定的业务场景,比如一年中的春、夏、秋、冬,还有每周的周一到周天,还有各种颜色,以及可以用它来描述一些状态信息,比如错...

一日一技:11个基本Python技巧和窍门

1.两个数字的交换.x,y=10,20print(x,y)x,y=y,xprint(x,y)输出:102020102.Python字符串取反a="Ge...

Python Enum 技巧,让代码更简洁、更安全、更易维护

如果你是一名Python开发人员,你很可能使用过enum.Enum来创建可读性和可维护性代码。今天发现一个强大的技巧,可以让Enum的境界更进一层,这个技巧不仅能提高可读性,还能以最小的代价增...

Python元组编程指导教程(python元组的概念)

1.元组基础概念1.1什么是元组元组(Tuple)是Python中一种不可变的序列类型,用于存储多个有序的元素。元组与列表(list)类似,但元组一旦创建就不能修改(不可变),这使得元组在某些场景...

你可能不知道的实用 Python 功能(python有哪些用)

1.超越文件处理的内容管理器大多数开发人员都熟悉使用with语句进行文件操作:withopen('file.txt','r')asfile:co...

Python 2至3.13新特性总结(python 3.10新特性)

以下是Python2到Python3.13的主要新特性总结,按版本分类整理:Python2到Python3的重大变化Python3是一个不向后兼容的版本,主要改进包括:pri...

Python中for循环访问索引值的方法

技术背景在Python编程中,我们经常需要在循环中访问元素的索引值。例如,在处理列表、元组等可迭代对象时,除了要获取元素本身,还需要知道元素的位置。Python提供了多种方式来实现这一需求,下面将详细...

Python enumerate核心应用解析:索引遍历的高效实践方案

喜欢的条友记得关注、点赞、转发、收藏,你们的支持就是我最大的动力源泉。根据GitHub代码分析统计,使用enumerate替代range(len())写法可减少38%的索引错误概率。本文通过12个生产...

Python入门到脱坑经典案例—列表去重

列表去重是Python编程中常见的操作,下面我将介绍多种实现列表去重的方法,从基础到进阶,帮助初学者全面掌握这一技能。方法一:使用集合(set)去重(最简单)pythondefremove_dupl...

Python枚举类工程实践:常量管理的标准化解决方案

本文通过7个生产案例,系统解析枚举类在工程实践中的应用,覆盖状态管理、配置选项、错误代码等场景,适用于Web服务开发、自动化测试及系统集成领域。一、基础概念与语法演进1.1传统常量与枚举类对比#传...

让Python枚举更强大!教你玩转Enum扩展

为什么你需要关注Enum?在日常开发中,你是否经常遇到这样的代码?ifstatus==1:print("开始处理")elifstatus==2:pri...

Python枚举(Enum)技巧,你值得了解

枚举(Enum)提供了更清晰、结构化的方式来定义常量。通过为枚举添加行为、自动分配值和存储额外数据,可以提升代码的可读性、可维护性,并与数据库结合使用时,使用字符串代替数字能简化调试和查询。Pytho...

78行Python代码帮你复现微信撤回消息!

来源:悟空智能科技本文约700字,建议阅读5分钟。本文基于python的微信开源库itchat,教你如何收集私聊撤回的信息。[导读]Python曾经对我说:"时日不多,赶紧用Python"。于是看...

登录人人都是产品经理即可获得以下权益

文章介绍如何利用Cursor自动开发Playwright网页自动化脚本,实现从选题、写文、生图的全流程自动化,并将其打包成API供工作流调用,提高工作效率。虽然我前面文章介绍了很多AI工作流,但它们...

Python常用小知识-第二弹(python常用方法总结)

一、Python中使用JsonPath提取字典中的值JsonPath是解析Json字符串用的,如果有一个多层嵌套的复杂字典,想要根据key和下标来批量提取value,这是比较困难的,使用jsonpat...

取消回复欢迎 发表评论: