Python教程035 模块module 与 包package
文章目录
模块与包
除了交互式编写python代码在Python解释器执行之外,还可以将代码写入后缀为py的文件。然后通过python命令编译执行。py文件就是模块。模块的名字就是该文件的名字(不包含后缀)。
Python 中(至少)有三类模块:
1.使用 Python 编写的模块(.py);
2.使用 C 编写的动态加载模块(.dll,.pyd,.so,.sl 等);
3.使用 C 编写并链接到解释器的模块,要获取此列表,输入:
|
|
输出结果
|
|
除了模块,还有包的概念。可以将包视为文件系统上的目录,将模块视为目录中的文件。与文件系统目录一样,包是按层次结构组织的,包本身可能包含子包以及模块。
重要的是要记住所有包都是模块,但并非所有模块都是包。换句话说,包只是一种特殊的模块。具体来说,任何包含path属性的模块都被视为包。
所有模块都有一个名称。子包名称与其父包名称用句点分开,类似于Python的标准属性访问语法。例如A.B.C,A是B的父包,B是C的父包。
import语句不是调用机制唯一的方法。importlib.import_module()和内置__import __()等函数也可用于调用导入机制。
import语句,from…import语句
通过导入,一个模块中的Python代码可以访问另一个模块中的代码。 import语句是调用导入机制的最常用方法。import语句进行两个操作;它搜索命名模块,然后将搜索结果绑定到本地。 import语句的搜索实际上是调用__import __()函数。 __import __()的返回值用于执行import语句的名称绑定操作。
首次导入模块时,Python会搜索模块,如果找到,它会创建一个模块对象,并对其进行初始化。如果找不到指定的模块,则引发ModuleNotFoundError。
用import或者from…import来导入相应的模块。有以下几种形式。
将整个包(package)导入,格式为:import <package>
将整个模块(module)导入,格式为:import <module>
从某个包中导入模块/子包/对象,格式为:from <package> import module or subpackage or object>
从某个模块中导入对象,格式为:from <module> import <object>
从某个模块中导入多个对象,格式为:from <module> import <object1>,<object2>,<object3>
将某个模块中的全部对象导入,格式为: from <module> import *
from <module> import *
可以导入像sayhi这样公用的名字,但不会导入version,因为它是以双下划线开始的特殊属性。不建议使用from import * ,有不可预知的风险
重命名模块/子包/对象
格式为:
from <package> import <module or subpackage or object> as myname
例如:
import sys as systemModule
常规包(regular packages)
Python定义了两种类型的包,常规包(regular packages)和命名空间包(namespace packages)。 常规包是Python 3.2及更早版本中存在的传统包。 常规包通常为包含init.py文件的目录。 导入常规包时,将隐式执行此init.py文件,并且它定义的对象将绑定到包命名空间中。__init__.py文件可以包含Python代码,并且Python将在导入模块时向模块添加一些其他属性。
例如,以下文件系统布局定义了一个包含三个子包的顶级父包parent:
|
|
导入parent.one将隐式执行parent/__ init__.py和parent/one/ __init__.py。 随后导入的parent.two或parent.three将分别执行parent/two/__init__.py和parent/three/__init__.py。
import module 时,模块中所有的代码将被执行(类对象,函数对象将被创建,不会被调用),import package时,init.py文件中的代码也将被执行。
命名空间包(namespace packages)
不包含init.py文件的目录,是命名空间包(namespace packages)。这一特性是在 Python 3.3 被引入的。
比如下面的目录结构:
.
├── configs
│ └── config.py
├── depends
├── tools
└──init.py
│ └── tool.py
如果要在tools/tool.py导入configs/config.py中的内容,在Python 3(3.3及之后) 中就可以直接使用from configs.config import * 来导入,Python 3.3以前就不可以,如果一定要这样做,就需要给configs文件夹下加入init.py才可以。
但是所谓的命名空间包提出的本意却不是说为了导入没有init.py 的文件夹的 Python 模块,而是利用命名空间包这个技术来导入目录分散的代码。
假设你有Python代码的两个不同的目录如下:
|
|
在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有init.py 文件。这时候再导入这两个包的时候:
|
|
两个不同的包目录被合并到一起,你可以导入spam.blah和spam.grok,并且它们能够工作。 在这里工作的机制被称为命名空间包的一个特征。从本质上讲,命名空间包是一种特殊的封装设计,为合并不同的目录的代码到一个共同的命名空间。
命名空间包的关键是确保顶级目录中没有init.py文件来作为共同的命名空间。缺失init.py文件使得在导入包的时候会发生有趣的事情:这并没有产生错误,解释器创建了一个由所有包含匹配包名的目录组成的列表。特殊的包命名空间模块被创建,只读的目录列表副本被存储在其path 变量中。
|
|
上面 configs 的例子其path变量如下:
|
|
在定位包的子组件时,目录path将被用到(例如, 当导入 spam.grok 或者 spam.blah 的时候或者 configs.config). 一个包是否被作为一个包命名空间的主要方法是检查其file 属性。如果没有,那包是个命名空间。这也可以由其字符表现形式中的 namespace 这个词体现出来。
|
|
模块的 file属性
导入模块时,可以通过模块的file属性查看模块所在磁盘的路径位置
|
|
包引用顺序
解释器会按照sys.path列表的顺序来查找被引入的包或模块名字。
|
|
优先加载当前工作目录下的模块,如果你的项目中使用了与内建模块中同名的包或模块名,就会遇到没有xx属性之类的报错提示。
你可以操作sys.path,使得其他路径文件加入到Path中,使之能被解释器发现。
|
|
由于找不到hi模块,会报错如下,
|
|
将hi模块加入到path中,
|
|
另外一种加载模块的方法:如果你的模块不在sys.path,还可以使用 imp 模块中的方法 imp.load_source
|
|
if __name__ == ‘__main__‘: 的解析
通过上面的讲解,我们知道Python文件既可以拿来执行,也可以用来作为模块使用import导入。当Python解析器读取一个源文件时它会执行所有的代码。在执行代码前会定义一些特殊的变量。如果解析器运行的模块(源文件)作为主程序,它将会把name变量设置成”__main__”。如果只是引入其他的模块,name变量将会设置成模块的名字,通常为模块文件名,不带路径或者文件扩展名。
在代码下方添加if __name__ == ‘__main__‘: 的主要原因是有时你需要你写的模块既可以直接的执行,还可以被当做模块导入到其他模块中去.通过检查是不是主函数,可以让你的代码只在它作为主程序运行时执行,而当其他人调用你的模块中的函数的时候不必执行。简单来说就是,方便我们代码复用,也可以测试模块。
举例说明:创建一个using_name.py文件,内容如下。
print(__name__)
通过python命令直接运行,python using_name.py
运行结果是main。
在Python的交互模式下运行,结果是using_name。
|
|
修改using_name.py,如下。
|
|
通过python命令直接运行,python using_name.py
运行结果是run itself。
在Python的交互模式下运行,结果是imported from another module。
|
|
转载请注明本网址。