找回密码
 会员注册
查看: 35|回复: 0

Python导入自己创建的包、模块

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
72245
发表于 2024-9-7 16:28:25 | 显示全部楼层 |阅读模式
模块、脚本、包首先明确几个概念,脚本、模块、包模块模块就是一系列python对象的集合,包括类的定义、函数、常量等,与C++中的hpp或h文件类似,(尽管有些可以运行)但是不被视作可以运行的文件。python官方手册中对于模块的解释如下:模块是包含Python定义和语句的文件。其文件名是模块名加后缀名 .py 。在模块内部,通过全局变量 __name__ 可以获取模块名(即字符串)脚本脚本是解释器解释运行的文件,通常会使用if__name__=="__main__":来运行主函数。与模块不同的是,无论脚本的名字如何,其__name__属性始终为__main__,即解释器将脚本作为视作__main__.py文件运行。包包一个层级嵌套式的文件(夹),可以类比操作系统中的文件夹进行理解。包是python为了帮助组织模块并提供名称层次结构。要注意的一个重点概念是所有包都是模块,但并非所有模块都是包。或者换句话说,包只是一种特殊的模块。特别地,任何具有__path__属性的模块都会被当作是包。对于包,目录下必须含有__init__.py文件,以表示当前目录是一个python包,__init__.py文件可以留空。包相对导入在包这里,包相对导入需要特别强调一下,包相对导入和linux下文件的相对路径类似,下面是python官方手册的解释:相对导入使用前缀点号。一个前缀点号表示相对导入从当前包开始。两个或更多前缀点号表示对当前包的上级包的相对导入,第一个点号之后的每个点号代表一级。运行python文件这里不使用特定的IDE,使用命令行环境运行python文件(脚本)。使用命令行运行python脚本的方式如下:python[-bBdEhiIOqsSuvVWx?][-ccommand|-mmodule-name|script|-][args]参数python有几个常用的命令行参数,如-c,-m,-I,-E等,以及不适用任何命令行参数运行脚本我呢间。这里主要讨论两种方式,即-m运行模块和直接运行脚本。-m使用-m参数运行python模块,文件(夹)之间的层属关系不再使用/或\,而是使用英文句号.,并且不能添加.py文件后缀名。需要特别注意的是,使用-m运行模块,与-c选项一样,当前目录将被加入环境变量sys.path的开头。对于模块,我们可以直接使用python-mmod1.mod11.mod111来运行mod1目录下的mod11子目录中的mod111.py模块。如果我们希望运行一个文件夹,比如上述的mod1或者mod11,我们也可以使用-m命令行参数运行。但是该包必须包含一个__main__.py文件,运行该包时,实际上python解释器运行的是其包含的__main__.py文件。以下是python官方文档中的说明:包名称(包括命名空间包)也允许使用。使用包名称而不是普通模块名时,解释器把  .__main__ 作为主模块执行。此行为特意被设计为与作为脚本参数传递给解释器的目录和zip文件的处理方式类似。pythonscript.py如果我们不适用-i或者-m参数,直接运行脚本(或者加入)-bBdEhiIOqsSuvVWx?等选项和一些参数argv。同样,我们可以使用pythondir的方式运行一个文件夹,并且解释器会运行该文件夹下的__main__.py文件。需要特别注意,这种运行方式会将文件所在目录加入sys.path的开头。(与-m区分开)实例下面使用一个实例来讨论几个问题。在linux环境下构建如下文件夹结构:parent├──__init__.py├──mod1│ ├──__init__.py│ ├──mod11│ │ ├──__init__.py│ │ └──mod111.py│ └──mod12.py├──mod2│ ├──__init__.py│ ├──__main__.py│ ├──mod21.py│ └──mod22.py└──mod3.py3directories,10files在parent文件夹内,我们将围绕mod21.py进行讨论。mod21.py的内容如下print(__name__)print(__spec__)importsysprint(sys.path)frommod22importhelloworld22frommod1.mod12importhelloworld12from..mod1import*from..mod1.mod12importhelloworld12from..mod1.mod11.mod111importhelloworld111from..mod3importhelloworld3from.mod22importhelloworld22helloworld12()helloworld111()helloworld3()helloworld22() 三个包中的__init__.py的内容如下(以mod1中为例,其余类似)if__name__=="__main__":print("mod1runsasmainprogramme")else:print("mod1runsasapackage")'运行运行parent/mod2/目录下执行pythonmod21.py此时报错ModuleNotFoundError:Nomodulenamed'mod1'mod22可以正确导入,因为在同一级目录下但是为什么mod1没有正确导入呢?请往下看~parent/目录下执行pythonmod2/mod21.py同样报错ModuleNotFoundError:Nomodulenamed'mod1'这是因为无论在哪个文件夹($pwd)内运行mod21.py,解释器向sys.path中添加的,始终都是parent/mod2/,即mod21.py所在的目录,所以无法看到mod1。解决方案1:使用sys.path.append导入mod1或者parent的路径。注意,如果导入mod1的路径,那么就无需frommod1import...了,直接导入mod1下面的模块。解决方案2:使用环境变量PYTHONPATH,其表示增加模块文件默认搜索路径。所用格式与终端的 ATH 相同。运行mod21.py前,使用export设置环境变量PYTHONPATHexportPYTHONPATH=parent随后mod1可以正确导入,且输出mod1runsasapackage但是接着报错Traceback(mostrecentcalllast): File"parent/mod2/mod21.py",line8,in  from..mod1import*ImportError:attemptedrelativeimportwithnoknownparentpackage ..mod1代表我们使用了包相对导入,但是这里为什么无法工作呢?在6.模块—Python3.12.3文档中,有这样一段话注意,相对导入基于当前模块名。因为主模块名永远是 "__main__" ,所以如果计划将一个模块用作Python应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。我们现在对此做详细解释。可以看到,mod21.py中第二行我们加入了一句print(__spec__)。__spec__是__main__.py文件中所独有的属性。这里我们通过测试,发现输出为None。只有当附加-m选项时,__spec__ 会被设为相应模块或包的模块规格说明。其余情况下均为None。正因为我们没有使用-m选项,所以这里的__spec__为None,代表mod21.py中代码不直接与可导入的模块相对应,即与其他.py文件不再具有包内相对位置的关系。所以我们无法使用包相对导入。只能使用绝对导入,即将mod1添加到sys.path中。parent/目录下运行python-mmod2.mod21Traceback(mostrecentcalllast): File"parent/mod2/mod21.py",line5,in  frommod22importhelloworld22ModuleNotFoundError:Nomodulenamed'mod22' 注意,使用-m选项,此时sys.path中添加的是命令行的工作环境$pwd。但是此时报错无法引入同一级中的mod22。注意到此时__spec__不再为None,而是ModuleSpec(name='mod2.mod21',loader=,origin='/mnt/d/Tmp/python/mod2/mod21.py') 那么我们使用绝对引用的方式导入mod22当然不成功,只能使用相对导入的方式。我们将这两行绝对导入的语句去掉,运行,结果还是报错Traceback(mostrecentcalllast): File"parent/mod2/mod21.py",line8,in  from..mod1import*ImportError:attemptedrelativeimportbeyondtop-levelpackage 从报错结果来看,无法导入mod1是因为相对导入的层级超过了当前包的层级,我们需要退回到parent的父级文件夹中运行parent父级目录下运行python-mparent.mod2.mod21此时运行正确,完整的输出为parentrunsasapackagemod2runsasapackagemod2__main__ModuleSpec(name='python.mod2.mod21',loader=,origin='/parent/mod2/mod21.py')['parent/..',]mod1runsasapackagepython.mod1.mod12Runningasapackagemod11python.mod1.mod11.mod111helloworldHelloWorldmod12HelloWorldmod111python.mod2.mod22HelloWorldmod12HelloWorldmod111HelloWorldmod3HelloWorldmod22参考参考python文档:5.导入系统—Python3.12.3文档一个module内的Python代码通过importing操作就能够访问另一个模块内的代码。import语句是发起调用导入机制的最常用方式,但不是唯一的方式。importlib.import_module()以及内置的__import__()等函数也可以被用来发起调用导入机制。import语句结合了两个操作;它先搜索指定名称的模块,然后将搜索结果绑定到当前作用域中的...https://docs.python.org/zh-cn/3/reference/import.html#special-considerations-for-main​​​​​​1.命令行与环境—Python3.12.3文档为获取各种设置信息,CPython解析器会扫描命令行与环境。CPython实现细节:其他实现的命令行方案可能会有所不同。详见其他实现。命令行:调用Python时,可以指定下列任意选项:最常见的用例是启动时执行脚本:接口选项:解释器接口类似于UNIXshell,但提供了额外的调用方法:Whencalledwithstandardinputconnec...https://docs.python.org/zh-cn/3.12/using/cmdline.html#environment-variables
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-11 05:51 , Processed in 0.452248 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表