发布于2020-03-12 12:36 阅读(1357) 评论(0) 点赞(5) 收藏(2)
目录
模块可以让小块代码轻松得以重新利用。当项目不断壮大,就会出现多种问题,需要重载的代码在物理逻辑上都会超出单个文件的合理大小。解决这个问题的方案是把有关联的模块组合到同一个包中。
模块是容纳代码的文件,一个模块定义了一组Python函数和其他对象,通常这些函数和对象都是关联的。模块的名称由文件名称而来。
包就是包含代码和子目录的目录。包里包含了一组通常相互关联的代码文件(模块)。包的名称由主目录名而来。
包是模块概念的自然扩展,旨在应付非常大型的项目。模块把相互关联的函数、类和变量进行了分组,同理,包则是把相互关联的模块进行了分组。
Python搜索模块的路径是由四部分构成的:程序的主目录、PATHONPATH目录、标准链接库目录和.pth文件的目录,这四部分的路径都存储在sys.path 列表中。
在导入自定义的模块时,除了指定模块名之外,也需要指定目录,由于Python把目录称作包,因此,这类导入被称为包导入。包导入把计算机上的目录变成Python的命名空间,而目录中所包含的子目录和模块文件则对应命名空间中的属性。
Python已经导入的模块保存在一个内置的sys.modules字典中,以便记录哪些模块已经记录了。
如果想看模块搜索路径在机器上的实际配置,可以通过打印内置的sys.path列表来查看,这个列表是sys模块的path属性:
- >>> import sys
- >>> sys.path
- ['', 'D:\\python37\\Lib\\idlelib', 'D:\\python37\\python37.zip', 'D:\\python37\\DLLs', 'D:\\python37\\lib', 'D:\\python37', 'C:\\Users\\administrator\\AppData\\Roaming\\Python\\Python37\\site-packages', 'D:\\python37\\lib\\site-packages']
这里用一个能够运行的例子来演示包机制的内部工作原理。文件名和路径将显示为普通文本,以便明确我们分析的是文件还是目录,还是由该文件/目录定义的模块/包。以下是这个mathproj包的树状结构图:
以下是这个包的代码:
- print("Hello from mathproj init")
- __all__ = ['comp']
- version = 1.03
- __all__ = ['c1']
- print("Hello from mathproj.comp init")
x = 1.00
print("Hello from numeric init")
- from mathproj import version
- from mathproj.comp import c1
- from mathproj.comp.numeric.n2 import h
- def g():
- print("version is ",version)
- print(h())
- def h():
- return "Called function h in module n2"
当我们把以上代码按照树状结构输入mathproj文件中后,还要确保mathproj在python的当前工作目录。
以上代码中能发现,每个文件夹中都有一个__init__.py文件,这个文件有两个用途:
很多包都不需要在其__init__文件中写入任何内容,只要保证有个空的__init__.py文件就行了。
当做好以上工作后,保证你的mathproj模块存在于你的IDLE模块检索路径之中时,在Python shell中执行以下语句:
- >>> import mathproj
- Hello from mathproj init
- >>> mathproj.version
- 1.03
如果一切顺利,Python将会执行__init__.py文件输出一段字符串,并且进入一个新的命令提示符,不会有错误信息。也就是说,第一次加载包的时候会自动执行__init__.py文件。
mathproj/__init__.py文件将把变量version赋值为1.03。变量version在mathproj包命名空间的作用域内,创建完毕后就可以通过mathproj访问,在mathproj/__init__.py文件之外也能够进行访问。
在使用过程中,包看起来与模块类似,都可以通过属性访问到其内部定义的对象。因为包就是模块的汇总。
mathproj包中的各个文件也是可以相互进行交互的。比如我们想访问mathproj/comp/nmeric/n1.py文件中定义的函数g,那么这个模块是否已经加载完毕了。上面已经导入了mathproj包,那么它的子包是否导入了呢:
- >>> mathproj.comp.numeric.n1
- Traceback (most recent call last):
- File "<pyshell#2>", line 1, in <module>
- mathproj.comp.numeric.n1
- AttributeError: module 'mathproj' has no attribute 'comp'
我们发现访问这个函数会进行报错,也就是说,只加载顶级模块是不够的,这并不会加载其全部子模块。这是符合Python理念的,即清晰比简洁更重要。
解决以上问题,只要导入所需模块即可,然后就可以执行该模块中的函数g了:
- >>> import mathproj.comp.numeric.n1
- Hello from mathproj init
- Hello from mathproj.comp init
- Hello from numeric init
- >>> mathproj.comp.numeric.n1.g()
- version is 1.03
- Called function h in module n2
不过这么导入的话,每一个包中的__init__.py文件都会被执行,每一个包都被导入,可以在其中产看上一级包的内存地址判断这个包是否被导入:
- >>> mathproj.comp
- <module 'mathproj.comp' from 'D:\\python37\\mathproj\\comp\\__init__.py'>
- >>> mathproj.comp.numeric
- <module 'mathproj.comp.numeric' from 'D:\\python37\\mathproj\\comp\\numeric\\__init__.py'>
包内的文件不会自动获得包内其他文件的访问权限,无法访问同一包内其他文件中定义的对象。我们来看一下n1.py文件中:
- from mathproj import version
- from mathproj.comp import c1
- from mathproj.comp.numeric.n2 import h
- def g():
- print("version is ",version)
- print(h())
n1.py文件想要访问其他包和文件中的version以及h函数,需要先进行导入。
因为n2.py和n1.py位于同一个目录中,所以还可以使用相对导入的方式,只要在子模块名前加一个.即可。也就是n1.py的第三行写成这样,一样可以奏效:
from .n2 import h
一般来说,如果外部代码执行了from module inport *语句,就应该从mathproj导入全部的非私有对象名称。实际上着比较难以实现主要问题是,对于不容的操作系统来说,对文件名的定义规则比较含糊,这就导致了子包导入后其确切名称也含糊不定。
例如,写的是from mathproj import *,那么comp会被导入为comp、Comp还是COMP呢?如果只是依赖系统给出的名称,那么结果是不可预测的。
为了解决这个问题,Python引入了__all__属性,__all__属性应该给出一个字符串列表,定义了如果外部代码对这个包使用from module import *时,应该导入的名称。如果没有定义这个属性的话,我们在使用from module import *时,不会对该包执行任何操作。
我们在上面定义的包中输入以下语句:
- >>> from mathproj import *
- Hello from mathproj init
- Hello from mathproj.comp init
- >>> comp
- <module 'mathproj.comp' from 'D:\\python37\\mathproj\\comp\\__init__.py'>
- >>> c1
- Traceback (most recent call last):
- File "<pyshell#2>", line 1, in <module>
- c1
- NameError: name 'c1' is not defined
- >>> from mathproj.comp import *
- >>> c1
- <module 'mathproj.comp.c1' from 'D:\\python37\\mathproj\\comp\\c1.py'>
从上面的代码中能看出来,使用from module import *导入时,和前面import module语句一样的规则。
导入包时,只是对这个包的名称,而不会导入这个包里面的包的名称,要想得到这个名称也需要进行一次显式的导入。
大多数包的结构,都不应该像以上例子暗示的那么复杂。有了Python包机制,包设计时的复杂程度和嵌套层次就能有很大的自由度了。
1,包的初始化
Python在首先导入某个目录时,会自动执行该目录下的__init__.py文件中的所有程序代码。
2,模块命名空间的初始化
在包导入模型中,脚本内的目录路径,在导入后会变成真实的对象路径,即为目录创建的模块对象提供了命名空间。
3,from *语句的行为
在__init__.py文件内使用__all__列表,来定义目录以from * 语句形式导入时,需要导出的属性清单。如果没有设置__all__,from *语句不会自动加载潜逃与该目录内的子模块,也就是说,只加载该目录下的__init__.py文件中罗列在__all__列表中的变量。
注意以下几点:
在Python中,所谓的库是由多个组件组成的,包括无须import语句即可使用的数值和列表之类的内置数据类型和常量,以及一些内置函数和异常。库中最大的部分就是大量的模块。只要安装了Python,就可以用库来操作各类数据和文件、与操作系统交互、为众多互联网协议编写服务端和客户端、开发和调试代码。
参考:
原文链接:https://blog.csdn.net/weixin_45755966/article/details/104084432
作者:我Lovepython
链接:https://www.pythonheidong.com/blog/article/254154/711e3170978f8601acf8/
来源:python黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 python黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-1
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!