Python篇--- Python 的加载、缓存、覆盖机制

要理解 import 与 if __name__ == "__main__": 的关系,以及 Python 的加载、缓存、覆盖机制,我们可以从 “模块的两种身份” 和 “导入的全过程” 入手,用通俗的例子一步步拆解。

一、核心:模块的 “双重身份” 与 __name__ 的作用

每个 .py 文件(模块)都有两种可能的 “身份”:

  • 作为 “主程序” 直接运行(比如 python script.py);
  • 作为 “模块” 被其他程序导入(比如 import script)。

__name__ 这个内置变量就是用来区分这两种身份的 “身份证”

  • 当模块直接运行时,__name__ 的值是 "__main__"
  • 当模块被导入时,__name__ 的值是模块名(比如 script

而 if __name__ == "__main__": 就像一个 “身份检查站”:只有当模块是 “主程序” 时,才会执行缩进内的代码(通常是程序的入口逻辑,比如 main() 函数)。

二、import 与 if __name__ 的核心关系:导入时 “不执行主程序”

import 的作用是 “加载模块并执行其顶层代码”,但 if __name__ == "__main__": 块内的代码是 “主程序专属逻辑”,导入时不会执行

我们用一个例子说清楚:

假设我们有两个文件:

  • tool.py(工具模块,可能被导入)
  • main.py(主程序,会导入 tool.py
1. tool.py 的内容:
# tool.py
print("tool.py 的顶层代码执行了")  # 顶层代码(未缩进)def add(a, b):return a + b# 主程序逻辑:只有直接运行 tool.py 时才执行
if __name__ == "__main__":print("tool.py 被直接运行了(主程序模式)")print(add(1, 2))  # 输出 3
2. main.py 的内容(导入 tool.py):
# main.py
print("开始导入 tool.py...")
import tool  # 导入 tool 模块
print("tool.py 导入完成")print(tool.add(3, 4))  # 使用 tool 中的函数
3. 两种运行场景的对比:
  • 场景 1:直接运行 tool.py
    命令:python tool.py
    输出:

    tool.py 的顶层代码执行了
    tool.py 被直接运行了(主程序模式)
    3
    

    解释:__name__ 是 "__main__",所以 if 块内的代码被执行。

  • 场景 2:运行 main.py(导入 tool.py
    命令:python main.py
    输出:

    开始导入 tool.py...
    tool.py 的顶层代码执行了  # 导入时执行了 tool.py 的顶层代码
    tool.py 导入完成
    7  # 成功使用 tool.add
    

    关键:导入 tool.py 时,只执行了它的顶层代码print 和 add 函数定义),但 if __name__ == "__main__": 块内的代码没有执行(因为此时 tool.py 的 __name__ 是 "tool",不是 "__main__")。

总结关系
import 会触发模块的 “顶层代码” 执行(定义函数、类、变量等),但 if __name__ == "__main__": 块内的代码是 “主程序专属逻辑”,仅在模块被直接运行时执行,导入时不执行。这确保了模块被导入时,不会意外执行其 “主程序代码”(比如测试逻辑、命令行交互等)。

三、加载机制:import 时模块是如何被 “读入” 的?

import 一个模块的过程,就像 “找文件→读内容→执行代码→存起来” 的流程,具体分 4 步:

  1. 检查缓存:先看这个模块是否已经导入过(存在 sys.modules 字典中)。如果有,直接用缓存的模块,不重复加载。
  2. 查找模块:如果没缓存,Python 会按 sys.path 列表中的路径(当前目录、标准库目录等)查找模块文件(.py.pyc 等)。
  3. 加载并执行:找到文件后,Python 会读取文件内容,执行其中的顶层代码(定义函数、类、变量,以及未缩进的语句),并生成一个 “模块对象”。
  4. 存入缓存:将生成的模块对象存入 sys.modules,方便下次导入时直接使用。
加载机制与 if __name__ 的互动:

在 “执行顶层代码” 这一步,模块中的所有未缩进代码都会被执行(包括 import 其他模块、定义函数等),但 if __name__ == "__main__": 块内的代码是否执行,取决于模块的 “身份”:

  • 若模块是被导入的(__name__ = 模块名):if 条件不成立,块内代码不执行。
  • 若模块是直接运行的(__name__ = "__main__"):if 条件成立,块内代码会被执行(属于顶层代码的一部分,只是被条件判断包裹了)。

四、缓存机制:为什么模块不会被重复加载?

Python 有一个 “模块缓存”(sys.modules 字典),用来存储已经导入的模块对象。第一次导入模块时会执行其代码并缓存,后续导入直接用缓存,不会重复执行代码

举例说明:

# main.py
import sys
print("第一次导入 tool:")
import tool  # 第一次导入,执行 tool 的顶层代码print("\n第二次导入 tool:")
import tool  # 第二次导入,直接用缓存,不执行代码# 查看缓存中是否有 tool
print("\n缓存中是否有 tool?", "tool" in sys.modules)  # 输出 True

运行 main.py 的输出:

第一次导入 tool:
tool.py 的顶层代码执行了  # 第一次导入时执行
第二次导入 tool:  # 第二次导入,无输出(未执行代码)
缓存中是否有 tool? True

缓存的意义

  • 提高效率:避免重复读取文件和执行代码,节省时间。
  • 保证一致性:多次导入的是同一个模块对象,模块内的全局变量状态会被保留(比如计数器不会重置)。

五、覆盖机制:命名冲突时谁会 “胜出”?

当导入的模块 / 成员与当前作用域的变量、函数重名时,会发生 “覆盖”(后定义的会覆盖先定义的)。这与 import 的语法和执行顺序有关。

1. 导入的成员覆盖本地变量
# main.py
a = 10  # 本地变量 afrom tool import a  # 从 tool 导入 a(假设 tool.py 中 a=20)
print(a)  # 输出 20(被导入的 a 覆盖了本地 a)
2. 后导入的成员覆盖先导入的
# main.py
from tool import add  # 假设 tool.add 是 a+bfrom other_tool import add  # other_tool.add 是 a*b(后导入)
print(add(2, 3))  # 输出 6(被后导入的 add 覆盖)
3. 模块名覆盖问题

如果你的脚本名与标准库模块名相同(比如 json.py),导入时会优先加载你的脚本,覆盖标准库模块

# 假设你有一个 json.py 文件
# main.py
import json  # 会导入你的 json.py,而不是标准库的 json

这会导致标准库功能无法使用,所以不要用标准库模块名命名自己的脚本

六、总结

  1. import 与 __name__ 的关系
    import 会执行模块的顶层代码,但 if __name__ == "__main__": 块内的代码仅在模块直接运行时执行,导入时不执行,避免 “主程序逻辑” 被意外触发。

  2. 加载机制
    导入模块时,Python 会按 “查缓存→找文件→执行顶层代码→存缓存” 的流程处理,__name__ 决定主程序块是否执行。

  3. 缓存机制
    模块导入后会存入 sys.modules,后续导入直接用缓存,不重复执行代码,保证效率和状态一致性。

  4. 覆盖机制
    命名冲突时,后定义 / 导入的对象会覆盖先定义 / 导入的,需注意避免与标准库或本地变量重名。

理解这些机制,能帮你更清晰地控制代码的执行时机,避免导入时的意外行为(比如重复执行、命名冲突)。

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

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

相关文章

Java设计模式之行为型模式(访问者模式)应用场景分析

访问者模式(Visitor Pattern)作为Java设计模式中的“隐形冠军”,常被开发者低估其价值。这一模式通过“双分派”机制巧妙解耦数据结构与操作,为复杂系统的扩展提供了强大武器。在大厂项目中,访问者模式往往出现在业务逻…

【IDEA】JavaWeb自定义servlet模板

方法一:(推荐去使用方法二,还能创建其它代码模板)使用servlet模板创建Servlet类如果创建时找不到servlet模板:File -> Project Structure然后应用 -> OK,如果还是找不到Servlet模板,看看项…

Linux选择

在内存中运行着的进程称为( 服务 )。负责控制systemd系统和服务管理器的工具为( systemctl )命令。systemd管理系统服务的基本单位是( unit )。分配和管理资源的基本单位是( 进程 &#xf…

【Redis学习路|第一篇】初步认识Redis

概要: 深入探讨NoSQL数据库的核心特性,对比传统关系型数据库的差异,重点介绍Redis作为内存数据库的优势与应用场景。 文章目录认识 NoSQLNoSQL vs SQL 对比1️⃣ 结构化 vs 非结构化2️⃣ 关联 vs 非关联3️⃣ 查询方式对比4️⃣ 事务特性5️⃣ 存储方式…

java局域网聊天室小项目架构思路

java局域网聊天室小项目架构思路 项目需求 创建一个局域网聊天系统,要求:用户在登录界面登录后进入聊天窗口界面,能实现多用户同时在线聊天,并且用户之间可以进行私聊 项目用到的技术栈 java网络编程java多线程java面向对象编…

vulhub-corrosion2靶机

1.安装靶机 https://download.vulnhub.com/corrosion/Corrosion2.ovahttps://download.vulnhub.com/corrosion/Corrosion2.ova 2.扫描IP 3.扫描端口 4.访问端口 首先访问一下80端口 访问一个8080端口发现是一个apache的页面 5.扫描目录与漏洞探测 那么我们扫描一下目录 80…

Mysql深入学习:慢sql执行

目录 慢查询日志 慢查询主要步骤 11种慢查询的场景分析 场景一:SQL 没有建立索引 场景二:索引未生效的典型原因 场景三:LIMIT 深分页导致性能下降 场景四:单表数据量过大导致 SQL 性能下降 场景五:ORDER BY 出现…

李宏毅深度学习教程 第8-9章 生成模型+扩散模型

【2025版】12 生成式对抗网络GAN 一 – 基本概念介紹_哔哩哔哩_bilibili 目录 1. GAN生成式对抗网络 2. GAN的训练 散度差异 3.WGAN 4.训练GAN 5. 如何客观评估GAN 6. 条件型生成(按照要求) 7. Cycle GAN(互转配对) 8. d…

1.8 axios详解

Axios的定义与核心特性Axios是一个基于Promise的现代化HTTP客户端库,主要用于在浏览器和Node.js 环境中发送HTTP请求,旨在简化异步数据交互流程。其核心特性如下:跨平台支持:在浏览器中通过XMLHttpRequest对象发送请求&#xff0c…

41.安卓逆向2-frida hook技术-过firda检测(五)-利用ida分析app的so文件中frida检测函数过检测

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:图灵Python学院 工具下载: 链接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwdzy89 提取码&#xff1…

安卓调javaScript Not find method “forceLogout“ implementatidsignature or namesp

核对一下是否实现对应的javaScript或者javaScript的方法参数对不对, 在这里插入图片描述我这里一开始实现了这个方法但是没有给参数,一直报异常,后台说token没给就查token的问题,最后发现是搞偏了,两个原因&#xff0c…

【Linux网络】:UDP(传输层协议)

目录 一、铺垫知识 1、传输层 2、端口号 2.1、五元组表示 一个进程通信 2.2、端口号范围划分 2.3、知名端口 2.4、查看端口号 2.5、问题 3、pidof & netstat 命令 ①netsate 命令 ②pidof命令 二、UDP协议 1、UDP协议格式 2、UDP报文 1.1、UDP数据封装的过…

Effective C++ 条款19: 设计class犹如设计type

Effective C 条款19:设计class犹如设计type核心思想:设计新的class时,应当像语言设计者设计内置类型一样慎重,考虑对象的创建、销毁、初始化、拷贝、类型转换等所有方面。 ⚠️ 1. 类设计的关键问题域 对象生命周期管理&#xff1…

《汇编语言:基于X86处理器》第11章 MS-Windows编程(3)

本章展示的是如何用32 位Microsoft Windows API进行控制台窗口编程。应用编程接口(API:ApplicationProgramming Interface)是类型、常数和函数的集合体,它提供了一种用计算机代码操作对象的方式。本章将讨论文本I/O、颜色选择、时间与日期、数据文件I/O,…

在 macOS 上通过 Docker 部署DM8 (ARM 架构)

概述 达梦数据库 (DM8) 无法直接在 Apple macOS 操作系统上原生安装,通常需要通过虚拟机(如 Parallels Desktop、VMware Fusion)进行部署。另一种更轻量级且受 macOS 支持的方案是利用 Docker 容器技术来构建开发与测试环境。本文档将详细介…

网络协议之路由是怎么回事?

写在前面 要想去外面的世界看看, 就离不了路由器,而路由器工作的原理就是路由,那么具体是怎么路由的呢?本文就一起来看下这部分内容。 1:路由的配置 配置一条路由无非就是在配置以下三个信息: 1:包要去哪里&#x…

2106. 摘水果,梳理思路

文章目录题目概要java 解法详解题目概要 在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列…

深入理解消息队列(MQ)核心原理与设计精髓

引言:从一个“不堪重负”的订单系统说起想象一个简化的电商下单流程:用户点击“下单”后,系统需要:在订单数据库中创建一条记录。调用库存服务,扣减商品库存。调用营销服务,给用户发放积分和优惠券。调用通…

前端手撕题总结篇(算法篇——来自Leetcode牛客)

链表指定区域反转 找到区间(头和为 for循环当**时)->反转链表(返回反转过后的头和尾)->连接 function reverseBetween( head , m , n ) {//preEnd&cur&nextStart cur.next断开if(mn)return head;const vHeadNode…

从Excel到工时管理系统:企业如何选择更高效的工时记录工具?

还在为手工统计员工工时而头疼吗?月末堆积如山的Excel表格、反复核对的数据、层出不穷的差错,这些问题正在拖慢企业的发展步伐。8Manage工时管理系统发现,传统手工记录不仅耗费大量人力,更让宝贵的工时数据难以转化为有效的管理决…