在此之前先确认一个概念是否弄清

模块命名空间

1. 目录结构

假设你有以下结构:

testpkg/__init__.pyfool.pymaybe.py

内容如下:

fool.py

# testpkg/fool.py
class Fool:pass

maybe.py

# testpkg/maybe.py
class Maybe:pass

__init__.py

(先什么也不写,空文件)


2. 只import子模块,不动__init__.py

在外部写测试代码:

import testpkg.fool
import testpkg.maybeprint(hasattr(testpkg, 'Fool')) # False
print(hasattr(testpkg, 'Maybe')) # Falseprint(hasattr(testpkg.fool, 'Fool')) # True
print(hasattr(testpkg.maybe, 'Maybe')) # True

解释:

  • testpkg.fool模块的命名空间里有Fool(因为在fool.py里定义了)
  • testpkg.maybe模块的命名空间里有Maybe
  • 但是testpkg(这个包)的命名空间里没有FoolMaybe,因为没把它们导入到包的顶层。

3. “模块命名空间”到底是什么?

  • 每个py文件(模块)加载后,Python会创建一个名字空间(实际是一个dict),存放所有在这个模块里定义的名字。
  • 可以通过模块名.__dict__看到它的命名空间。

例如:

import testpkg.fool
import testpkg.maybeprint(testpkg.fool.__dict__.keys())
print(testpkg.maybe.__dict__.keys())
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'Fool'])
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'Maybe'])

4. 只有import到包,名字才会出现在包的命名空间

如果你修改testpkg/__init__.py为:

from testpkg.fool import Fool
from testpkg.maybe import Maybe__all__ = ["Fool", "Maybe"]
  • import testpkg 的时候,实际上会执行 testpkg/__init__.py 里的代码。
  • from testpkg.fool import Fool 这行代码的作用是:
    testpkg.fool 这个模块里的 Fool,导入到当前命名空间(也就是 testpkg 的命名空间)下。
  • 同理,from testpkg.maybe import MaybeMaybe 导入到 testpkg 命名空间下。

现在再测试:

import testpkgprint(hasattr(testpkg, 'Fool')) # True
print(hasattr(testpkg, 'Maybe')) # Trueprint(testpkg.__dict__.keys())
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'fool', 'Fool', 'maybe', 'Maybe', '__all__'])

此时FooBar被“绑定”到了testpkg这个包的命名空间下。

补充个__all__讲解

__all__ 是一个导出控制列表,是一个字符串列表。

当你这样用的时候:

from testpkg import *

Python 会只导入 __all__ 里列出的名字

也就是说:

  • 如果 __init__.py 里有 __all__ = ['Fool', 'Maybe']
  • 那么 from testpkg import * 只会导入 FoolMaybe
  • 其他即使你在 __init__.py 里定义了,也不会随 * 导出。

注意

  • __all__ 只影响 from ... import * 这种用法。
  • 普通的 import testpkg 不受影响。

非常好,你对模块命名空间、以及 __init__.py 的作用已经理解得很清楚了!下面我会在你的基础上,进一步系统性讲解 __init__.py 文件,帮助你更深入理解它的地位与用法。


__init__.py 文件

一、__init__.py 的本质

  • __init__.py包的初始化脚本
  • 只要目录下有 __init__.py,Python 才会把这个目录当做一个(Python 3.3+ 以后支持“隐式命名空间包”,但强烈建议有 __init__.py,便于兼容和控制行为)。
  • 当你 import testpkg 时,Python 实际执行 testpkg/__init__.py,并把里面的内容放进 testpkg 这个模块(包)对象的命名空间。

二、__init__.py 的作用

1. 标识包

  • 没有 __init__.py,Python 2 不认这个目录是包,会报错。
  • Python 3.3+ 的确可以没有(隐式包),但有了__init__.py能更清楚、兼容、可控。

2. 包初始化

  • 你可以在 __init__.py 里写包初始化逻辑,比如设置全局变量、初始化状态、打印调试信息等。

3. 控制包的“顶层接口”(API 设计

  • 你可以在 __init__.py 里导入(或重新命名)子模块、类、函数,让用户用更简单的方式访问包内容。
  • 例如:
    # testpkg/__init__.py
    from .fool import Fool
    from .maybe import Maybe
    
    这样用户可以 from testpkg import Fool,不用知道子模块结构。

4. 控制 from testpkg import * 导出的内容

  • 通过定义 __all__ 列表,决定哪些名字会被 * 导出。
    __all__ = ['Fool', 'Maybe']
    

5. 可以导入子包、子模块

  • 你可以在 __init__.py 里导入子包、子模块,甚至重命名,隐藏实现细节。
    from . import fool as _fool
    

三、实践举例

1. 最简单的情况

# 空文件
  • 只起到“标识包”的作用。

2. 聚合包的接口(对外API)

# testpkg/__init__.py
from .fool import Fool
from .maybe import Maybe
__all__ = ['Fool', 'Maybe']
  • 这样 testpkg.Fooltestpkg.Maybe 就变成包的“顶层接口”。

3. 初始化逻辑

# testpkg/__init__.py
print("testpkg包被导入了!")
_config = {"debug": True}
  • 导入包时会输出一句话,设置包级别的配置。

4. 导出模块而不是类

# testpkg/__init__.py
from . import fool
from . import maybe
__all__ = ['fool', 'maybe']
  • 这样 from testpkg import fool 就可以直接用 testpkg.fool.Fool

四、几点注意

  1. 相对导入和绝对导入

    • from .fool import Fool:点号代表“当前包”,推荐包内部用相对导入。
    • from testpkg.fool import Fool:绝对导入,避免循环依赖出错。
  2. 循环引用问题

    • 包内部如果互相引用,要注意不要在模块顶层出现死循环引用,否则会报 ImportError
  3. __init__.py 并不是必须的,但建议始终写上

    • 这样会让代码更清晰,有更好的兼容性。
  4. 包结构的“封装”思路

    • 你可以只暴露有限的接口给用户(通过__init__.py),隐藏实现细节。

五、你可以这样理解

  • 包(package) = 文件夹 + __init__.py
  • 包的命名空间 = __init__.py 的命名空间
  • 你让包有哪些“顶级名字”,就在 __init__.py 里加

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

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

相关文章

四核 A53+工业级存储:移远 SC200L 与 pSLC SD NAND 如何重构 T-BOX 性能边界?

博客目录 一、移远 SC200L:T-BOX 的 “智慧大脑”二、米客方德 MKDN064GIL-ZA T-BOX:数据安全的坚固堡垒三、深度协同:拓展 T-BOX 应用边界 在车联网浪潮席卷而来的当下,T-BOX 作为汽车与外界交互的核心枢纽,其性能优劣…

JavaEE-统一功能处理

拦截器 实现强制登录的功能, 后端程序根据Session来判断⽤⼾是否登录, 但是实现⽅法是⽐较⿇烦的 需要修改每个接⼝的处理逻辑 需要修改每个接⼝的返回结果 接⼝定义修改, 前端代码也需要跟着修改 有没有更简单的办法, 统⼀拦截所有的请求, 并进⾏Session校验呢, 这⾥我们学…

vscode运行c++文件和插件的方法

1.运行c文件全过程 VSCode运行C全教程-CSDN博客 按照以上的操作即可完成正常的配置流程。但是在运行我的文件时,总是出现终端和输出混乱的情况,我想要在终端中进行输入输出的话,需要加一个改动:设置--输入Run In Terminal--勾选…

利用云效实现自动化部署gitee仓库中的项目

本文主要介绍如何利用云效 实现Node项目(vue/react....)自动化部署 1.准备工作 Git 仓库【Gitee】 云服务器【华为云】 你的项目 2. 创建目录 服务器上创建两个目录 一个专门用来放压缩包: /home/www/dist (aaa.tgz bbb.tgz&am…

Flink SourceFunction深度解析:数据输入的起点与奥秘

在Flink的数据处理流程中,StreamGraph构建起了作业执行的逻辑框架,而数据的源头则始于SourceFunction。作为Flink数据输入的关键组件,SourceFunction负责从外部数据源读取数据,并将其转换为Flink作业能够处理的格式。深入理解Sour…

LabVIEW 共享变量通讯方式

在LabVIEW 开发中,共享变量(SharedVariable)作为实现数据实时交换的关键技术,广泛应用于 LabVIEW、PLC 编程、分布式 SCADA 系统等领域。解析主流共享变量通讯机制的技术原理、性能特性及工程实践中的选型策略。​ 一、Network -P…

Angular进阶之十二:Chrome DevTools+Angular实战诊断指南

引言 最近有一个工单是说用户在使用我们的系统的时候,如果使用某个页面的次数多了以后浏览器就开始变慢甚至卡死崩溃掉。这个问题明显是提示有内存泄露,今天就由这个问题开始分享一些关于内存泄漏的知识。 一、 Web 应用内存泄漏的危害与易忽略性 危害&…

在云服务器上搭建 MinIO 图片存储服务器及 Spring Boot 整合实现图片上传下载

一、MinIO 核心概念 MinIO 是一个高性能的分布式对象存储服务器,兼容 Amazon S3 API,具有以下特点: 高性能:针对存储和检索优化 轻量级:单个二进制文件即可运行 云原生:支持 Kubernetes 部署 S3 兼容&a…

《深入解析:如何通过CSS集成WebGPU实现高级图形效果》

当CSS的细腻笔触遇上WebGPU的磅礴算力,两者如同命运交织的织工,以代码为丝线,在虚拟空间中编织出超越现实维度的灵境。这场融合不再局限于视觉呈现的革新,而是创造出一种能够与用户情感共鸣、突破物理法则束缚的沉浸式数字体验&am…

R 语言科研绘图 --- 环状图-汇总

在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…

突破限制:实现页面内精准监听 localStorage 变更

突破限制:实现页面内精准监听 localStorage 变更 一、简介二、示例演示三、StorageEvent重构setItem四、CustomEvent自定义事件同一页面不同模块数据同步五、MessageChannel同一页面不同模块数据同步六、BroadcastChannel多窗口数据同步七、CustomEventBroadcastCha…

牛客AI面试破解电销招聘效率与成本双重难题

在电销行业,高流动性与大规模招聘需求长期困扰企业人力资源管理。传统招聘模式下,HR需应对海量简历筛选、多轮面试协调、主观评估偏差等挑战,导致招聘周期长、成本高、人才匹配度低。如何通过技术手段实现精准筛选与效率提升?牛客…

智慧生产管控数字化平台(源码+文档+讲解+演示)

引言 在全球化和信息化的浪潮中,制造业正面临着前所未有的挑战和机遇。智慧生产管控数字化平台应运而生,旨在通过数字化手段优化生产管控的全流程。本文将详细介绍智慧生产管控数字化平台的核心功能、技术架构以及如何通过开源代码实现二次开发&#xf…

用Tensorflow进行线性回归和逻辑回归(九)

用TensorFlow训练线性和逻辑回归模型 这一节结合前面介绍的所有TensorFlow概念来训练线性和逻辑回归模型,使用玩具数据集。 用TensorFlow训练模型 假如我们指明了数据点和标签的容器,定义了张量操作的损失函数。添加了优化器节点到计算图,…

使用 vue vxe-table 实现复选框禁用,根据行规则来禁用是否允许被勾选选中

使用 vue vxe-table 实现复选框禁用,根据行规则来禁用是否允许被勾选选中 查看官网:https://vxetable.cn 禁用选中 通过 checkMethod 方法控制 checkbox 是否允许用户手动勾选,如果被禁用,可以调用 setCheckboxRow 方法手动设置…

【Linux-网络】深入拆解TCP核心机制与UDP的无状态设计

🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 道阻且长,行则将至 目录 📚引言 📚一、UDP协议 📖 1.概述 📖 2.特点 &#x1…

(nice!!!)(LeetCode 每日一题) 2081. k 镜像数字的和 (枚举)

题目:2081. k 镜像数字的和 思路:枚举10进制的回文串,然后来判断对应的k进制数是否是回文串。直到有n个满意即可。 而枚举10进制的回文串,从基数p(1、10、100… )开始,长度为奇数的回文串,长度为偶数的回文…

Java面试题027:一文深入了解数据库Redis(3)

Java面试题025:一文深入了解数据库Redis(1) Java面试题026:一文深入了解数据库Redis(2) 本节我们整理一下Redis高可用和消息队列使用场景的重点原理,让大家在面试或者实际工作中遇到这类问题时…

算法打卡 day4

4 . 高精度算法 性质:数组或者容器从低位往高位依次存储大整数,方便进位。 4.1 高精度加法 给定两个正整数(不含前导 0),计算它们的和。 输入格式 共两行,每行包含一个整数。 输出格式 共一行,…

【笔记】Docker 配置阿里云镜像加速(公共地址即开即用,无需手动创建实例)

2025年06月25日记 【好用但慎用】Windows 系统中将所有 WSL 发行版从 C 盘迁移到 非系统 盘的完整笔记(附 异常处理)-CSDN博客 【笔记】解决 WSL 迁移后 Docker 出现 “starting services: initializing Docker API Proxy: setting up docker ap” 问题…