Python_misc<一>Package和module
(2023-08-15 22:06:35)
标签:
python |
分类: Python |
作者:Sam (甄峰) sam.zhen1977@outlook.com
1. Python module(模块):
1.1:module:
Python Module(模块)是一个python文件,以.py结尾。包含了Python对象和Python语句。
模块中能定义函数,class, 变量。 模块中也可以包含可执行的代码。
函数,class,变量可以看成是一块块积木,而一个模块中可以包含多个函数,变量和class,可以看成是一盒积木。
例如,建立一个python文件:module_sample.py
内容如下:
#!/usr/bin/python3
# -*- coding:utf-8 -*-
#这里会显示在 __doc__中
"""
This is module sample document.
"""
def print_func(par):
print ("Hello : " , par)
return
Money = 25000
def addMoney(val):
global Money
Money = Money + val
__all__ = ["addMoney"]
print("This is module_sample.py")
if __name__ == "__main__":
print(Money)
addMoney(300)
print(Money)
print("This is module_sample program")
print_func(2)
想要调用此module内的函数或变量。可以使用import.
import导入module的方式有两种:
A.
使用这种语法格式的import方式,会导入指定module中所有成员(变量,函数, class). 使用被导入的module中的成员时,需要使用module_name(或者别名)作为前缀。否则Python解释器会报错。
B.
使用这种语法格式的import方式,只会导入模块中指定的的成员。需要使用该成员时,无需附加任何前缀,直接使用成员名或者别名即可。这种方式还有个特别方式:
from module_name import *
1.2: import (方式A):
如果另一个同目录的python程序(test_module.py)想要引入此模块,可以采用import。
#!/usr/bin/python3
# -*- coding:utf-8 -*-
import
module_sample
#此时调用需要加入模块名
module_sample.print_func("test_module.py")
print(module_sample.Money)
module_sample.addMoney(1000)
print(module_sample.Money)
#import的本质
print(type(module_sample))
print(module_sample)
执行之:
#python
test_module.py
结果:
This is module_sample.py
Hello
:
25000
26000
此时,需要调用module中的函数时,需要: 模块名.函数名
注意:
A. 不管执行多少次import, 一个模块只被导入一次.
B. 导入模块时,会把模块执行一遍,并生成一个模块对象,类型为 class module。被导入模块中定义的函数,变量,class等会添加到这个模块对象的属性里。 所以我们调用这些函数,变量时,需要通过module调用,因为它就是这个模块对象的属性。
1.3:
from ... import
这个语句让程序从一个模块中导入指定部分
#!/usr/bin/python3
# -*- coding:utf-8 -*-
from module_sample import print_func
print_func("import module mode2")
结果为:
This is module_sample.py
Hello
:
注意:
A. 不管执行多少次import, 一个模块只被导入一次.
B. 导入模块时,会把模块执行一遍。同时把指定的函数,变量或class导入到当前命名空间。
1.4:模块的搜索路径:
Python在import module时,有时会出现ModuleNotFoundError: No module named '模块名'。
原因时Python找不到这个模块。 为什么呢,因为Python会遵循一定机制找Python 模块。而咱们要导入的模块不在Python寻找的位置。
当import或者from .... import时,Python解析器对模块位置的搜索顺序是:
1. 当前目录。
2. shell的 PYTHONPATH目录。
3.
以上所有涉及到的目录,被保存在sys的sys.path变量中。通过此变量,我们可以看到指定程序查找的所有目录。所以当要导入的模块没有存储在sys.path显示的目录中,那要导入该模块时,就会出现找不到模块的异常。
解决方法也很简单。
A. 向sys.path中临时添加模块文件存储位置的路径。
B. 把模块文件放在sys.path变量中已经包含的模块加载路径中。
C. 设置path环境变量。
建立一个新python(module_utils.py),
要导入另一个目录内的module(
import sys
import module_sample
#M_Utils_Test1.py 放在 M_Utils/M_Utils_Test1.py。 所以先把目录加入sys.path
sys.path.append(r"M_Utils")
import M_Utils_Test1
print(module_sample.__all__)
print(module_sample.__doc__)
print(sys.path)
1.5: module的 __all__, __doc__, __file__:
A. __all__ 变量,该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。通过在模块文件中设置 __all__ 变量,当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 __all__ 列表中指定的成员。
其它import方式不受__all__影响。
def addMoney(val):
#
def __showMoney_1():
#只对form modeule_name import *
#和 显式使用 __all__有效
__all__ = ["addMoney"]
print 一个module的 __all__ 可以看到它支持的所有成员(函数,变量和class).
B. __file__:
print(module_sample.__file__)
通过调用 __file__ 属性输出的绝对路径,我们可以很轻易地找到该模块(或包)的源文件
C. help和__doc__:
虽然通过 __all__.
可以看到模块所有的成员(函数,变量和class),
当并不清楚这些名称代表了什么意思,也不清楚他们的功能。所以就有了
help(module_sample)
Help on module module_sample:
NAME
FUNCTIONS
DATA
FILE
注意:
之所以我们可以使用 help() 函数查看具体成员的信息,是因为该成员本身就包含表示自身身份的说明文档(本质是字符串,位于该成员内部开头的位置)。前面讲过,无论是函数还是类,都可以使用 __doc__ 属性获取它们的说明文档,模块也不例外。
如果使用 help() 函数或者 __doc__ 属性,仍然无法满足我们的需求,还可以使用以下 2 种方法:
1.
2.
2. Python Package(包):
2.1:Package:
实际开发中,一个大型项目通常有大量Python模块,如果把模块放在一起,管理起来很困难。 同时,模块虽然可以有效的避免变量名和函数名重名引起的冲突,但如果模块之间重名了怎么办? 于是就有了Package。
Package(包)是一个分层次的文件目录结构,它定义了一个由module(模块)及子包和子包下的子包组成的Python的应用环境。
包是个文件夹,此文件夹必须存在__init__.py文件。 文件内容可以为空, 这个文件的存在用于标识当前文件夹是个Package(也叫常规包,与命名空间包区别).
当一个常规包Package被导入时, 这个__init__.py文件被隐式执行。
__init__.py 不同于其他模块文件,此模块的模块名不是 __init__,而是它所在的包名。例如,在 settings 包中的 __init__.py 文件,其模块名就是 settings。
引入Package中的module时,可以:
from package.module import func
举例如下:
建立目录:P_Utils作为package. 与之同级目录,建立:test_package.py
P_Utils目录内,建立三个文件:__init__.py
内容分别为:
__init_.py:
# -*- coding:utf-8 -*-
if __name__ == '__main__':
else:
P_Utils_1.py :
#!/usr/bin/python3
# -*- coding:utf-8 -*-
def utils_1_func():
print ("This is P_Utils_1.py")
return
P_Utils_2.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
def utils_2_func():
print ("This is P_Utils_2.py")
return
test_package.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
from P_Utils.P_Utils_1 import utils_1_func
from P_Utils.P_Utils_2 import utils_2_func
if __name__ == "__main__":
utils_1_func()
utils_2_func()
结果如下:
Package P_Utils init
This is P_Utils_1.py
This is P_Utils_2.py
可以看到,要导入package的module。 会先隐式调用__init__.py.
2.2 Package包的导入:
包本质上也是模块,所以导入方法和模块的导入有类似:
A. import Package_Name[.Module_name [as 别名]]
B. from Package_Name import Module_Name [as 别名]
C. from Package_Name.Module_Name import 成员名 [as 别名]
2.2.1: 方法A。 import Package_Name[.Module_name [as 别名]]
2.2.1.1:导入包中的模块:
通过此语法格式导入包中的指定模块后,在使用该模块中的成员(变量、函数、类)时,需添加“包名.模块名”为前缀
#Blog中方法A
import P_Utils.P_Utils_1
if __name__=="__main__":
结果:
Package P_Utils init
This is P_Utils_1.py
F:\work\current\Source\svn_Sam_2023\Linux_Tool_Utils\trunk\python\Python_Misc\2_Package\P_Utils\P_Utils_1.py
解析:
导入Package中的Module,和直接导入Module一样。会把模块执行一遍,并生成一个模块对象,类型为 class module。被导入模块中定义的函数,变量,class等会添加到这个模块对象的属性里。 所以我们调用这些函数,变量时,需要通过module调用,因为它就是这个模块对象的属性。
2.2.1.2:只导入包:
import Package_Name
只导入包名, 程序会自动执行该包下 __init__.py文件中的代码,但并不意味着Package中的其它Module会被import .
例如:
import P_Utils
print(type(P_Utils))
print(P_Utils.__file__)
可以看到:
F:\work\current\Source\svn_Sam_2023\Linux_Tool_Utils\trunk\python\Python_Misc\2_Package\P_Utils\__init__.py
即:只导入包名,它也会建立一个Module. 只是这个Module是 __init__.py
2.2.2:方法B:
from Package_Name import Module_Name [as 别名]
#Blog中方法B:
from P_Utils import P_Utils_1, P_Utils_2
if __name__=="__main__":
结果:
ackage P_Utils init
This is P_Utils_1.py utils_1_func()
This is P_Utils_2.py utils_2_func
F:\work\current\Source\svn_Sam_2023\Linux_Tool_Utils\trunk
2.2.3:方法C:
from Package_Name.Module_Name import 成员名 [as 别名]
#Blog中方法C:
from P_Utils.P_Utils_1 import utils_1_func
if __name__=="__main__":
结果为:
Package P_Utils init
This is P_Utils_1.py utils_1_func()
3.
3.1import的运作机制:
当我们使用import 某个Module时,Python会首先在sys.modules中搜索此Module名的模块,如果sys.modules中存在此模块,则将缓存映射的内容直接返回。import流程结束。
如果缓存中没有此模块,Python会依次搜索内置模块列表,sys.path列表定义的路径中搜索该模块。
当Python搜索到该模块时,会在本地作用域内初始化相应的module对象,并把模块中的成员以属性的形式赋给module. 这样,就可以使用该模块的各个成员(变量,函数,class)了。
3.2:绝对导入和相对导入:
A.
B.
绝对导入的参照物是项目的根文件夹。即使用从项目的根文件夹到要导入的模块的完整路径。
4. Script(脚本):
一个Python文件,要么是个脚本(Script),要么是个模块(module), 这取决于此Python文件的使用方式。
若直接运行Python文件,则称之为运行一个python script.
若在另一个Python文件中import这个文件或文件中的对象,函数或class. 则此Python文件称之为module.
这里也解释下常见的一个python参数。
-m mod run library module as a script (terminates option list)
即“-m”选项后面的内容是 module(模块),其作用是把模块当成脚本来运行。
“terminates option list”意味着“-m”之后的其它选项不起作用,在这点上它跟“-c”是一样的,都是“终极选项”。
Python解释器会查找名为name的module或者package。并将其内容当做 “__main__”来执行。
附1:
She-bang(#!):用来在Linux/unit系统中指定程序程序的,又叫hashbang. 就是一个 #!.
shebang符号通常在Linux/Unix系统的脚本中第一行的开头写,它指明了执行这个脚本文件的解释程序。(解释程序必须使用绝对路径)
A. 如果脚本中没有#!这一行,则执行时会默认使用当前shell去解释这个脚本。
B. 如果脚本中 #!
指定的解释程序是一个可执行文件,则执行这个脚本时,会把这个文件名和参数一起作为参数传递给解释程序去执行。
C. 如果 #!指定的解释程序没有执行权限,则报错 无权限。
D. 如果#!指定的解释程序不是一个可执行程序,则会被忽略,转而调用SHELL去解释。
E.
如果#!指定的解释程序不存在。则会报错。
以Python程序为例,如果没有 shebang. 则必须显式的指定解释程序:
# python
she_bang.py
如果添加了shebang,并修改she_bang.py为可执行权限。
#!/usr/bin/python3
print("This is
she_bang test program.")
则可以直接执行:
#./she_bang.py
在Windows下,系统利用后缀名判断文件类型,所以.py程序会自动关联到python程序去执行,she_bang不起任何作用。只被视为普通注释。
附2:
# -*- coding:utf-8 -*-
Python
2.x版本的py文件一般默认的是ASCII码,如果文件里有中文,运行时会出现乱码,注释是中文也不行。因此,需要把文件编码类型改为utf-8的类型,输入#
-*- coding:utf-8 -*-之后会把文件编码强制转换为utf-8。 Python
3.x版本的py文件的默认编码为Unicode,也就是说不用进行编码声明,可以直接使用中文了。