首页 > 编程语言 >Python 中的 __init__.py 和__all__ 详解(抄袭的joker) 因为写的实在是太好了

Python 中的 __init__.py 和__all__ 详解(抄袭的joker) 因为写的实在是太好了

时间:2023-11-03 16:33:48浏览次数:55  
标签:__ sub4 Python py init test import

Python 中的 __init__.py 和__all__ 详解

JOKER JOKER 没意思先生  

之前不论是自己写代码还是用别人的代码,都没有注意过这个东西,今天忽然看了一下,网上的教程感觉讲的都不是很清楚,自己又研究了研究,总结一下,如果有不对的地方,大家帮忙指正一下。

在Python工程里,当python检测到一个目录下存在__init__.py文件时,python就会把它当成一个模块(module)。个人习惯说这是一个包,只有.py的文件我才说是模块,不知道我这个表述规范不规范,不规范的话大家帮忙指正一下。

__init__.py这个东西不是必须有的,如果有的话,在调用包的时候,会运行这个文件。没有的话,也不耽误。

__init__.py存在的意义,是简化代码

看这个示例:

# my_test.py

from test2.sub4.test41 import print_41 # print_41是在test41.py里定义的函数 后面的都可以依次类推 我就不写了

print_41()

跑的通,没任何问题

这样也跑的通:

from test2.sub4 import test41
test41.print_41()

但这个,是跑不通的,不是把py文件写在了一个文件夹下,这个文件夹就是知道他下面有啥了的!!!!

from test2 import sub4
sub4.test41.print_41()

# 报错:AttributeError: module 'test2.sub4' has no attribute 'test41'

下面调整一下,加上__init__.py,但是__init__.py里什么都不写

再跑这段代码:

from test2 import sub4
sub4.test41.print_41()

不好意思,这样照样也是跑不通,会报同样的错误,回到我前面说的,在调用包的时候,会运行__init__.py这个文件,你__init__里啥也没写,就跟没有一样

那要怎么样这段代码才能跑的通呢?在两个__init__文件里分别写上这些东西:

# test2/__init__.py
from . import sub4

# test2/sub4/__init__.py
from . import test41

然后运行那一段代码,就可以跑的通了,原理就是,我在运行 from test2 import sub4 这句代码的时候,就运行了 test2/sub4/__init__.py (其实test2/__init__.py也有运行) 就相当于在sub4这个包里,引入了test41这个模块,sub4知道自己有这个模块了。

这里还有一个,我为什么要写 from . import test41 直接 import 不行吗?不行的,在我们执行 import 时,当前目录是不会变的(就算是执行子目录的文件),还是需要完整的路径的。from . 的意思,就是在当前运行文件的所在目录下面找。

所以 我如果运行下面这个:

from test2 import sub4
sub4.test42.print_42()

跑不通的, test2/sub4/__init__.py没有写 import test42,所以sub4是不知道自己下面有test42这个模块的

继续 为什么我说是运行了那两个__init__.py文件呢,我们稍微修改一下__init__.py文件:

# test2/__init__.py
from . import sub4
print("you have imported sub4")

# test2/sub4/__init__.py
from . import test41
print("you have imported test41")

再来运行这个代码:

from test2 import sub4
sub4.test41.print_41()

# 输出:
# you have imported test41
# you have imported sub4
# 41 (函数.print_41的输出)

看到了吧 from test2 import sub4 照样会运行 test2/__init__.py 这个文件

哎 反正涉及到 test2 就会运行 test2/__init__.py,就可以import sub4,import sub4 不就又运行了 test2/sub4/__init__.py 那我这样写不也可以吗:

import test2
test2.sub4.test41.print_41()

当然可以了,完全跑的通,输出也依然是:

# you have imported test41
# you have imported sub4
# 41 (函数.print_41的输出)

但这样可不行:

import test2
sub4.test41.print_41()

冷不丁的写一个sub4,是不行的,在my_test.py里sub4是没有被定义的。

但是这样写好长啊,有没有办法短一点呢?有办法的,把 test2/__init__.py 改成这个:

# test2/__init__.py
from .sub4.test41 import print_41 as p41
print("you have imported sub4")

然后 我就只需要写,就可以了:

import test2
test2.p41()

# 输出
# you have imported test41
# you have imported sub4
# 41 (函数.print_41的输出)

其实一般情况下,init文件都是这样写的,不然写了init 我们还要写一大堆东西,那写init干嘛呢。

 

接下来 还要说一个变量,就是__all__:

看上边这个东西

# test/__init__.py
from .sub1 import *
from . import sub2
from . import sub3
__all__=['sub1','sub3']
print("You have imported test")

# test/sub1/__init__.py
from . import test11,test12
__all__ = ['test11']
print("you have imported sub1")

# test/sub2/__init__.py
from . import test21
from . import test22
print('you have imported sub2')

# sub3 里没有init文件

然后我们分别运行下面的代码 我直接把输出都列出来了:

from test import *
print(dir())

# 输出:
# you have imported sub1
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub1', 'sub3']
# 看好了,输出了 you have imported sub2 ,dir()没有sub2,原因就是,sub2 不在__all__这个变量里。那为什么会输出那句话呢,原因就是 运行了test/__init__.py

import test
print(dir())
print(dir(test))
print(dir(test.sub1))
print(dir(test.sub2))
print(dir(test.sub3))
test.test11.print_11()

# 输出:
# you have imported sub1
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub1', 'sub2', 'sub3', 'test11']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub1', 'sub2', 'test11']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'test11', 'test12']
# ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
# 11

# 我们发现,在当前的py文件下,只有test这个包,但在test包中,不仅有 'sub1', 'sub2', 'sub3',还有一个 'test11'模块,并且可以直接test.test11.print_11()调用里面的函数
# 我们还发现,test.sub1包里照样有test11和test12两个,test.sub2里也照样有test21和test22两个,sub3下面啥也没有
# 挨个解释,只有当 from test import * 的时候,__all__才有影响。当直接import test 时,__all__是影响不到test中子包的。dir(test)里,是有sub2的
# 为什么test下会有 test11,因为在init里,写的是 from .sub1 import * ,这就相当于把sub1包里的模块导入了test包里,所以可以直接调用
# 为什么test下没有 test12呢,因为test12 不在 test/sub1/__init__.py 的__all__变量里。
# 为什么test.sub1下会有test11和test12两个,原因还是__all__是影响不到包里面的模块的。
# 为什么sub3下面啥也没有,因为sub3里是没有init的,他是不知道自己下边有啥的

加深一下理解,把 test/__init__.py 改一下,改成这样:

# test/__init__.py
from . import sub2
from . import sub3

__all__=['sub1','sub3']
print("You have imported test")

运行下面这个:

import test
print(dir())
print(dir(test))

# 输出
# you have imported sub2
# You have imported test
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
# ['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'sub2', 'sub3']
# 完全没有sub1什么事了,原因就是在test里没有import sub1,test下是没有那个子包的

from test import *
print(dir())
# 输出
# you have imported sub2
# You have imported test
# you have imported sub1
['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'sub1', 'sub3']
# 哎!这下有了sub1了,但注意you have imported sub1这句话跑到最后了。原因是,__all__变量起了作用,并且是在运行完了init之后,才把sub1给弄进来的

标签:__,sub4,Python,py,init,test,import
From: https://www.cnblogs.com/donefive/p/17807882.html

相关文章

  • 文心一言 VS 讯飞星火 VS chatgpt (127)-- 算法导论11.2 2题
    二、用go语言,位向量(bitvector)是一个仅包含0和1的数组。长度为m的位向量所占空间要比包含m个指针的数组少得多。请说明如何用一个位向量来表示一个包含不同元素(无卫星数据)的动态集合。字典操作的运行时间应为O(1)。文心一言,代码正常运行:在Go语言中,我们可以使用map[int]bool来......
  • 【pycharm】pycharm中添加allure库及pip升级
    参考:https://blog.csdn.net/weixin_45464162/article/details/129910922一、本地下载allure包并配置环境变量1、访问 https://github.com/allure-framework/allure2/releases,下载allure并解压到电脑本地,解压位置可自定义,, 2、环境变量中配置allure的bin目录  3、通过......
  • 音视频常见问题(七):首开慢
    摘要本文主要讨论音视频应用中的首开慢问题,文章介绍了首开慢的产生原因:DNS解析耗时、网络传输协议耗时、传输网络调度耗时,并提供了排查方式和解决方案。即构科技的ExpressSDK和MSDN网络可以有效的解决首开慢问题,且节省开发成本。一、前言对于音视频开发者来说,掌握排查问题的技......
  • 创建自定义美颜滤镜:使用第三方美颜SDK的步骤指南
    美颜滤镜在现代移动应用和直播平台中变得越来越受欢迎。它们可以让用户在自拍照片、视频聊天或实时直播中看起来更加美丽和自信。如果您是一位应用开发者,想要增加美颜滤镜功能,但又不想从头开始构建整个系统,那么使用第三方美颜SDK可能是一个明智的选择。第1步:选择适合的第三方美颜SD......
  • 离线快速LCA(最近公共祖先) Tarjan算法
    离线快速LCA(最近公共祖先)Tarjan算法前言对于OIer来说,LCA一直是处理树上问题的好帮手,无论是倍增还是树剖都有着优秀的\(\logn\)的复杂度。不过由于我们(数据规模)的上进,需要更快速求LCA,于是就有了……反正之前打死我都不相信这玩意能离线,还能O(1)算法思路首先来一棵树:......
  • IBM业务流程与IT信息中心战略规划报告 P80
    本人从事咨询工作多年,二十年一线数字化规划咨询经验,提供制造业数智化转型规划服务,顶层规划/企业架构/数据治理/数据安全解决方案资料干货.该PPT共90页,由于篇幅有限,以下为部分资料,如需完整原版 方案,点击右上角红色按钮关注+私信。本文来源于网络,侵权立删流程与信息化部门在现代企......
  • APM建设踩了哪些坑?去哪儿旅行分布式链路追踪系统实践
    一分钟精华速览分布式链路追踪系统在企业的APM体系中扮演着重要的角色。本文分享了去哪儿旅行构建分布式链路追踪系统的实践经验。从APM整体架构设计入手,讲述了日志收集、Kafka传输和Flink任务处理等环节的性能优化实践和踩坑经验。同时,作者结合丰富的分布式系统架构经验,探讨了AP......
  • 企业财务数字化转型的机遇有哪些?_光点科技
    随着数字技术的飞速发展,企业正面临着一个前所未有的转型机会。尤其在财务领域,数字化不仅仅是一种技术进步,更是一个全面提升企业竞争力的战略选择。那么,企业财务数字化转型所带来的机遇有哪些?提高效率与生产力数字化技术如自动化、机器学习和人工智能可以极大地提高财务部门的工作效......
  • 知识付费系统小程序开发中的最新趋势和技术是什么?
    在迅速发展的移动应用市场中,知识付费系统小程序成为了在线学习和知识传递的重要形式。随着技术的不断进步,了解最新的趋势和技术对于开发知识付费系统小程序至关重要。本文将讨论当前在这一领域中备受关注的最新趋势和技术。1.跨平台开发框架的崛起使用诸如ReactNative、Flutter和......
  • 华为云API Explorer伙伴招募火热进行中~
    API编排新特性助力伙伴零代码构建API工作流实现快速集成场景编排能力面向三大核心场景:API调用逻辑频繁改动API间高耦合不便管理复杂API解决方案难构建伙伴加入可立享四大限时福利:免费试用、专家咨询、技术指导、生态支持详情请点击下方海报查看**点此报名>>**https://survey.hu......