Python Module(模块)及包
一、Python 模块(Module)
Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。
模块让你能够有逻辑地组织你的 Python 代码段。
把相关的代码分配到一个模块里能让你的代码更好用,更易懂。
模块能定义函数,类和变量,模块里也能包含可执行的代码。
下例是个简单的模块 support.py:
def print_func( par ):
print("Hello : ", par)
return
二、Python包(存放多个模块的文件夹)
实际开发中,一个大型的项目往往需要使用成百上千的
Python
模块,如果将这些模块都堆放在一起,势必不好管理。而且,使用模块可以有效避免变量名或函数名重名引发的冲突,但是如果模块名重复怎么办呢?因此,Python提出了包(Package)的概念。
什么是包呢?简单理解,包就是文件夹,只不过在该文件夹下必须存在一个名为“__init__.py” 的文件。
注意,这是 Python 2.x 的规定,而在 Python 3.x 中,__init__.py 对包来说,并不是必须的。
每个包的目录下都必须建立一个 __init__.py 的模块,可以是一个空模块,可以写一些初始化代码,其作用就是告诉 Python 要将该目录当成包来处理。
注意,__init__.py 不同于其他模块文件,此模块的模块名不是 __init__,而是它所在的包名。例如,在 settings 包中的 __init__.py 文件,其模块名就是 settings。
包是一个包含多个模块的文件夹,它的本质依然是模块,因此包中也可以包含包。例如,在前面章节中,我们安装了 numpy 模块之后可以在 Lib\site-packages 安装目录下找到名为 numpy 的文件夹,它就是安装的 numpy 模块(其实就是一个包),它所包含的内容如图 1 所示。
图 1 numpy包(模块)
从图 1 可以看出,在 numpy 包(模块)中,有必须包含的 __init__.py 文件,还有 matlib.py 等模块源文件以及 core 等子包(也是模块)。这正印证了我们刚刚讲过的,包的本质依然是模块,包可以包含包。
Python 库:相比模块和包,库是一个更大的概念,例如在 Python 标准库中的每个库都有好多个包,而每个包中都有若干个模块。
三、导入模块
3.1 创建模块
模块即是 .py 文件
3.2 import 语句
使用“import fk_module”
导入模块的本质
就是,将 fk_module.py 中的全部代码加载到内存并执行,然后将整个模块内容赋值给与模块同名的变量,该变量的类型是 module,而在该模块中定义的所有程序单元都相当于该 module 对象的成员。
模块定义好后,我们可以使用 import 语句来引入模块,语法如下:
import module1[, module2[,... moduleN]]
比如要引用模块 math,就可以在文件最开始的地方用
import math
来引入。在调用 math 模块中的函数时,必须这样引用:
模块名.函数名
当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。
搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support.py,需要把命令放在脚本的顶端:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 导入模块
import support
# 现在可以调用模块里包含的函数了
support.print_func("Runoob")
以上实例输出结果:
Hello : Runoob
一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。
3.3 from…import 语句
使用“from fk_module import name, hello”
导入模块中成员的本质
就是将 fk_module.py 中的全部代码加载到内存并执行,然后只导入指定变量、函数等成员单元,并不会将整个模块导入,因此上面程序在输出 fk_module 时将看到错误提示:
name 'fk module' is not defined
。
Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中。语法如下:
from modname import name1[, name2[, ... nameN]]
例如,要导入模块 fib 的 fibonacci 函数,使用如下语句:
from fib import fibonacci
这个声明不会把整个 fib 模块导入到当前的命名空间中,它只会将 fib 里的 fibonacci 单个引入到执行这个声明的模块的全局符号表。
3.4 from…import* 语句(此方式不推荐)
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。
例如我们想一次性引入 math 模块中所有的东西,语句如下:
from math import *
在导入模块后,可以在模块文件所在目录下看到一个名为“__pycache__”的文件夹,打开该文件夹,可以看到 Python 为每个模块都生成一个 *.cpython-36.pyc 文件,比如 Python 为 fk_module 模块生成一个 fk_ module.cpython-36.pyc 文件,该文件其实是 Python 为模块编译生成的字节码,用于提升该模块的运行效率。
四、Python创建包、导入包
4.1 Python创建包
包其实就是文件夹,更确切的说,是一个包含“__init__.py”文件的文件夹。因此,如果我们想手动创建一个包,只需进行以下 2 步操作:
新建一个文件夹,文件夹的名称就是新建包的包名;
在该文件夹中,创建一个 __init__.py 文件(前后各有 2 个下划线‘_’),该文件中可以不编写任何代码。当然,也可以编写一些 Python初始化代码,则当有其它程序文件导入包时,会自动执行该文件中的代码。
例如,现在我们创建一个非常简单的包,该包的名称为 my_package,可以仿照以上 2 步进行:
创建一个文件夹,其名称设置为 my_package;
在该文件夹中添加一个 __init__.py 文件,此文件中可以不编写任何代码。不过,这里向该文件编写如下代码:
http://c.biancheng.net/
创建第一个 Python 包
print
(
'
http://c.biancheng.net/python/
'
)
可以看到,__init__.py 文件中,包含了 2 部分信息,分别是此包的说明信息和一条 print 输出语句。
由此,我们就成功创建好了一个 Python 包。
创建好包之后,我们就可以向包中添加模块(也可以添加包)。这里给 my_package 包添加 2 个模块,分别是 module1.py、module2.py,各自包含的代码分别如下所示(读者可直接复制下来):
#module1.py模块文件
def display(arc):
print(arc)
#module2.py 模块文件
class CLanguage:
def display(self):
print("http://c.biancheng.net/python/")
现在,我们就创建好了一个具有如下文件结构的包:
my_package
┠── __init__.py
┠── module1.py
┗━━ module2.py
当然,包中还有容纳其它的包,不过这里不再演示,有兴趣的读者可以自行调整包的结构。
4.2 Python导入包
通过前面的学习我们知道,包其实本质上还是模块,因此导入模块的语法同样也适用于导入包。无论导入我们自定义的包,还是导入从他处下载的第三方包,导入方法可归结为以下 3 种:
import 包名[.模块名 [as 别名]]
from 包名 import 模块名 [as 别名]
from 包名.模块名 import 成员名 [as 别名]
用 [] 括起来的部分,是可选部分,即可以使用,也可以直接忽略。
注意,导入包的同时,会在包目录下生成一个含有 __init__.cpython-36.pyc 文件的 __pycache__ 文件夹。
1) import 包名[.模块名 [as 别名]]
以前面创建好的 my_package 包为例,导入 module1 模块并使用该模块中成员可以使用如下代码:
import my_package.module1
my_package.module1.display("http://c.biancheng.net/java/")
运行结果为:
http://c.biancheng.net/java/
可以看到,通过此语法格式导入包中的指定模块后,在使用该模块中的成员(变量、函数、类)时,需添加“包名.模块名”为前缀。当然,如果使用 as 给包名.模块名”起一个别名的话,就使用直接使用这个别名作为前缀使用该模块中的方法了。
import my_package.module1 as module
module.display("http://c.biancheng.net/python/")
程序执行结果为:
http://c.biancheng.net/python/
另外,当直接导入指定包时,程序会自动执行该包所对应文件夹下的 __init__.py 文件中的代码。
import my_package
my_package.module1.display("http://c.biancheng.net/linux_tutorial/")
直接导入包名,并不会将包中所有模块全部导入到程序中,它的作用仅仅是导入并执行包下的 __init__.py 文件,因此,运行该程序,在执行 __init__.py 文件中代码的同时,还会抛出 AttributeError 异常(访问的对象不存在)。
http://c.biancheng.net/python/
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\demo.py", line 2, in <module>
my_package.module1.display("http://c.biancheng.net/linux_tutorial/")
AttributeError: module 'my_package' has no attribute 'module1'
我们知道,包的本质就是模块,导入模块时,当前程序中会包含一个和模块名同名且类型为 module 的变量,导入包也是如此:
import my_package
print(my_package)
print(my_package.__doc__)
print(type(my_package))
运行结果为:
http://c.biancheng.net/python/
<module 'my_package' from 'C:\\Users\\mengma\\Desktop\\my_package\\__init__.py'>
http://c.biancheng.net/
创建第一个 Python 包
<class 'module'>
2) from 包名 import 模块名 [as 别名]
仍以导入 my_package 包中的 module1 模块为例,使用此语法格式的实现代码如下:
from my_package import module1
module1.display("http://c.biancheng.net/golang/")
运行结果为:
http://c.biancheng.net/python/
http://c.biancheng.net/golang/
可以看到,使用此语法格式导入包中模块后,在使用其成员时不需要带包名前缀,但需要带模块名前缀。
当然,我们也可以使用 as 为导入的指定模块定义别名。
from my_package import module1 as module
module.display("http://c.biancheng.net/golang/")
此程序的输出结果和上面程序完全相同。
同样,既然包也是模块,那么这种语法格式自然也支持
from 包名 import *
这种写法,它和 import 包名 的作用一样,都只是将该包的 __init__.py 文件导入并执行。
3) from 包名.模块名 import 成员名 [as 别名]
此语法格式用于向程序中导入“包.模块”中的指定成员(变量、函数或类)。通过该方式导入的变量(函数、类),在使用时可以直接使用变量名(函数名、类名)调用。
from my_package.module1 import display
display("http://c.biancheng.net/shell/")
运行结果为:
http://c.biancheng.net/python/
http://c.biancheng.net/shell/
当然,也可以使用 as 为导入的成员起一个别名。
from my_package.module1 import display as dis
dis("http://c.biancheng.net/shell/")
该程序的运行结果和上面相同。
另外,在使用此种语法格式加载指定包的指定模块时,可以使用 * 代替成员名,表示加载该模块下的所有成员。
from my_package.module1 import *
display("http://c.biancheng.net/python")
4.3 Python __init__.py作用
前面章节中,已经对包的创建和导入进行了详细讲解,并提供了大量的实例,这些实例虽然可以正常运行,但存在一个通病,即为了调用包内模块的成员(变量、函数或者类),代码中包含了诸多的 import 导入语句,非常繁琐。
要解决这个问题,就需要搞明白包内 __init__.py 文件的作用和用法。
我们知道,导入包就等同于导入该包中的 __init__.py 文件,因此完全可以在 __init__.py 文件中直接编写实现模块功能的变量、函数和类,但实际上并推荐大家这样做,因为包的主要作用是包含多个模块。因此 __init__.py 文件的主要作用是导入该包内的其他模块。
也就是说,通过在 __init__.py 文件使用 import 语句将必要的模块导入,这样当向其他程序中导入此包时,就可以直接导入包名,也就是使用
import 包名
(或
from 包名 import *
)的形式即可。
上节中,我们已经创建好的 my_package 包,该包名包含 module1 模块、module2 模块和 __init__.py 文件。现在向 my_package 包的 __init__.py 文件中编写如下代码:
# 从当前包导入 module1 模块
from . import module1
#from .module1 import *
# 从当前包导入 module2 模块
#from . import module2
from .module2 import *
可以看到,在 __init__.py 文件中用点(.)来表示当前包的包名,除此之外,from import 语句的用法和在程序中导入包的用法完全相同。
总的来说,__init__.py 文件是通过如下 2 种方式来导入包中模块的:
# 从当前包导入指定模块
from . import 模块名
# 从.模块名 导入所有成员到包中
from .模块名 import *
第 1 种方式用于导入当前包(模块)中的指定模块,这样即可在包中使用该模块。当在其他程序使用模块内的成员时,需要添加“包名.模块名”作为前缀,例如:
import my_package
my_package.module1.display("http://c.biancheng.net/python/")
运行结果为:
http://c.biancheng.net/python/
第 2 种方式表示从指定模块中导入所有成员,采用这种导入方式,在其他程序中使用该模块的成员时,只要使用包名作为前缀即可。例如如下程序:
import my_package
clangs = my_package.CLanguage()
clangs.display()
运行结果为:
http://c.biancheng.net/python/
五、搜索路径及Python找不到指定模块的解决办法
通常情况下,当使用 import 语句导入模块后,Python 会按照以下顺序查找指定的模块文件:
在当前目录,即当前执行的程序文件所在目录下查找;
到 PYTHONPATH(环境变量)下的每个目录中查找;
到 Python 默认的安装目录下查找。
以上所有涉及到的目录,都保存在标准模块 sys 的 sys.path 变量中,通过此变量我们可以看到指定程序文件支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常。
解决“Python找不到指定模块”的方法有 3 种,分别是:
向 sys.path 中临时添加模块文件存储位置的完整路径;
将模块放在 sys.path 变量中已包含的模块加载路径中;
设置 path 系统环境变量。
六、PYTHONPATH 变量
作为环境变量,PYTHONPATH 由装在一个列表里的许多目录组成。PYTHONPATH 的语法和 shell 变量 PATH 的一样。
在 Windows 系统,典型的 PYTHONPATH 如下:
set PYTHONPATH=c:\python27\lib;
在 UNIX 系统,典型的 PYTHONPATH 如下:
set PYTHONPATH=/usr/local/lib/python
七、命名空间和作用域
变量是拥有匹配对象的名字(标识符)。命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。
一个 Python 表达式可以访问局部命名空间和全局命名空间里的变量。如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。
每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。
Python 会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的。
因此,如果要给函数内的全局变量赋值,必须使用 global 语句。
global VarName 的表达式会告诉 Python, VarName 是一个全局变量,这样 Python 就不会在局部命名空间里寻找这个变量了。
例如,我们在全局命名空间里定义一个变量 Money。我们再在函数内给变量 Money 赋值,然后 Python 会假定 Money 是一个局部变量。然而,我们并没有在访问前声明一个局部变量 Money,结果就是会出现一个 UnboundLocalError 的错误。取消 global 语句前的注释符就能解决这个问题。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
Money = 2000
def AddMoney():
# 想改正代码就取消以下注释:
# global Money
Money = Money + 1
print Money
AddMoney()
print Money
八、dir()函数
dir() 函数一个排好序的字符串列表,内容是一个模块里定义过的名字。
返回的列表容纳了在一个模块里定义的所有模块,变量和函数。如下一个简单的实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 导入内置math模块
import math
content = dir(math)
print content;
以上实例输出结果:
['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan',
'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp',
'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh',
'sqrt', 'tan', 'tanh']
在这里,特殊字符串变量__name__指向模块的名字,__file__指向该模块的导入文件名。
九、globals() 和 locals() 函数
根据调用地方的不同,globals() 和 locals() 函数可被用来返回全局和局部命名空间里的名字。
如果在函数内部调用 locals(),返回的是所有能在该函数里访问的命名。
如果在函数内部调用 globals(),返回的是所有在该函数里能访问的全局名字。
两个函数的返回类型都是字典。所以名字们能用 keys() 函数摘取。
十、reload() 函数
当一个模块被导入到一个脚本,模块顶层部分的代码只会被执行一次。
因此,如果你想重新执行模块里顶层部分的代码,可以用 reload() 函数。该函数会重新导入之前导入过的模块。语法如下:
reload(module_name)
在这里,module_name要直接放模块的名字,而不是一个字符串形式。比如想重载 hello 模块,如下:
reload(hello)
十一、Python中的包
包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。
简单来说,包就是文件夹,但该文件夹下必须存在 __init__.py 文件, 该文件的内容可以为空。
__init__.py
用于标识当前文件夹是一个包。
考虑一个在
package_runoob
目录下的
runoob1.py、runoob2.py、__init__.py
文件,test.py 为测试调用包的代码,目录结构如下:
test.py
package_runoob
|-- __init__.py
|-- runoob1.py
|-- runoob2.py
源代码如下:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def runoob1():
print("I'm in runoob1")
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def runoob2():
print(I'm in runoob2)
现在,在
package_runoob
目录下创建
__init__.py
:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
if __name__ == '__main__':
print('作为主程序运行')
else:
print('package_runoob 初始化')
然后我们在
package_runoob
同级目录下创建 test.py 来调用
package_runoob
包
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# 导入 Phone 包
from package_runoob.runoob1 import runoob1
from package_runoob.runoob2 import runoob2
runoob1()
runoob2()
以上实例输出结果:
package_runoob 初始化
I'm in runoob1
I'm in runoob2
如上,为了举例,我们只在每个文件里放置了一个函数,但其实你可以放置许多函数。你也可以在这些文件里定义Python的类,然后为这些类建一个包。
参考:Python 模块 -
https://www.runoob.com/python/python-modules.html