模块与包
# 简介
python 提供了强大的模块支持,主要体现在,不仅 Python 标准库中包含了大量的模块(称为标准模块),还有大量的第三方模块,开发者自己也可以开发自定义模块。通过这些强大的模块可以极大地提高开发者的开发效率。
# 1. 模块
# 1.1 导入模块
# import
import 模块名
模块名.变量 模块名.函数 模块名.类
import 模块名 as 别名
2
3
4
# from
from 模块名 import 变量/函数/类
from 模块名 import * 该模块中所有的内容
但是如果想限制获取的内容,可以在模块使用__all__[使用*可以访问到内容]
from 模块名 import 成员名 as 别名
from tbjx import read1,read2,name # 导入多个模块
2
3
4
5
6
7
8
无论是import还是from的形式,都会将模块内容进行加载
# 限制模块函数的调用__main__
不想让模块中的某些代码执行, 可以用__name__属性来使程序仅调用模块中的一部分。
每一个模块中都有一个__name__属性, 当其值等于__main__时, 表明该模块自身在执行, 否则被引入了其他文件。
当前文件如果为程序的入口文件, 则__name__属性的值为__main__。
if __name__ == '__main__':
print('这是Titan模块')
else:
def sayGood():
print('good')
def sayNice():
print('nice')
def sayBad():
print('bad')
age = 20
name = 'titan'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# *模块的搜索路径*
模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
#模块的查找顺序
1、在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用
ps:python解释器在启动时会自动加载一些模块到内存中,可以使用sys.modules查看
2、如果没有,解释器则会查找同名的内置模块
3、如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。
#需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错。
#在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。
>>> import sys
>>> sys.path.append('/a/b/c/d')
>>> sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索
注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理,
#首先制作归档文件:zip module.zip foo.py bar.py
import sys
sys.path.append('module.zip')
# sys.path获取到的是一个列表.所以我们可以执行列表的操作
import foo
import bar
#也可以使用zip中目录结构的具体位置
sys.path.append('module.zip/lib/python')
#windows下的路径不加r开头,会语法错误
sys.path.insert(0,r'C:\\Users\\Administrator\\PycharmProjects\\a')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.2 编写自定义模块
'''
add:相加
Subtraction:减法
'''
number = 100
__all__ = ['add', 'subtraction'] # 只让调用者访问这二个方法 ,这个__all__是为from x import *号设置的
def add(*args):
if len(args) > 1:
sum = 0
for i in args:
sum += i
return sum
else:
print("参数一定要大于一位数add")
return 0
def subtraction(*args):
if len(args) > 1:
m = args[0] * 2 # 提供args的第一个元素*2在减第一个,就变成了正数
for i in args:
m -= i
return m
else:
print("参数一定要大于一位数add")
return 0
__all__ = ['add', 'subtraction'] # 只让调用者访问这二个方法 ,这个__all__是为from x import *号设置的
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# __all__变量用法
all 变量,该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。通过在模块文件中设置 all 变量,当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 all 列表中指定的成员。
也就是说,只有以“from 模块名 import *”形式导入的模块,当该模块设有 all 变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。
def say():
print("人生苦短,我学Python!")
def CLanguage():
print("C语言中文网:<http://c.biancheng.net>")
def disPython():
print("Python教程:<http://c.biancheng.net/python>")
__all__ = ["say","CLanguage"]
# 可见,__all__ 变量只包含 say() 和 CLanguage() 的函数名,不包含 disPython() 函数的名称。此时直接执行 test.py 文件,其执行结果为:
人生苦短,我学Python!
C语言中文网:<http://c.biancheng.net>
Traceback (most recent call last):
File "C:/Users/mengma/Desktop/2.py", line 4, in <module>
disPython()
NameError: name 'disPython' is not defined
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
再次声明,all 变量仅限于在其它文件中以“from 模块名 import *”的方式引入。也就是说,如果使用以下 2 种方式引入模块,则 all 变量的设置是无效的。
以“import 模块名”的形式导入模块。通过该方式导入模块后,总可以通过模块名前缀(如果为模块指定了别名,则可以使用模快的别名作为前缀)来调用模块内的所有成员(除了以下划线开头命名的成员)。
#demo.py def say(): print("人生苦短,我学Python!") def CLanguage(): print("C语言中文网:<http://c.biancheng.net>") def disPython(): print("Python教程:<http://c.biancheng.net/python>") __all__ = ["say"] #test.py import demo demo.say() demo.CLanguage() demo.disPython() 人生苦短,我学Python! C语言中文网:<http://c.biancheng.net> Python教程:<http://c.biancheng.net/python>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17以“from 模块名 import 成员”的形式直接导入指定成员。使用此方式导入的模块,all 变量即便设置,也形同虚设。
from demo import say from demo import CLanguage from demo import disPython say() CLanguage() disPython() 人生苦短,我学Python! C语言中文网:<http://c.biancheng.net> Python教程:<http://c.biancheng.net/python>
1
2
3
4
5
6
7
8
9
10
# 2. 包
实际开发中,一个大型的项目往往需要使用成百上千的Python (opens new window)模块,如果将这些模块都堆放在一起,势必不好管理。而且,使用模块可以有效避免变量名或函数名重名引发的冲突,但是如果模块名重复怎么办呢?因此,Python提出了包(Package)的概念。
什么是包呢?简单理解,包就是文件夹,只不过在该文件夹下必须存在一个名为“init.py” 的文件。
注意,这是 Python 2.x 的规定,而在 Python 3.x 中,init.py 对包来说,并不是必须的。
每个包的目录下都必须建立一个 init.py 的模块,可以是一个空模块,可以写一些初始化代码,其作用就是告诉 Python 要将该目录当成包来处理。
注意,init.py 不同于其他模块文件,此模块的模块名不是 init,而是它所在的包名。例如,在 settings 包中的 init.py 文件,其模块名就是 settings。
# 2.1 创建包,导入包
# 创建包
《Python包 (opens new window)》一节中已经提到,包其实就是文件夹,更确切的说,是一个包含“init.py”文件的文件夹。因此,如果我们想手动创建一个包,只需进行以下 2 步操作:
- 新建一个文件夹,文件夹的名称就是新建包的包名;
- 在该文件夹中,创建一个 init.py 文件(前后各有 2 个下划线‘_’),该文件中可以不编写任何代码。当然,也可以编写一些 Python (opens new window) 初始化代码,则当有其它程序文件导入包时,会自动执行该文件中的代码(本节后续会有实例)。
# 导入包
通过前面的学习我们知道,包其实本质上还是模块,因此导入模块的语法同样也适用于导入包。无论导入我们自定义的包,还是导入从他处下载的第三方包,导入方法可归结为以下 3 种:
import 包名[.模块名 [as 别名]]
from 包名 import 模块名 [as 别名]
from 包名.模块名 import 成员名 [as 别名]
用 [] 括起来的部分,是可选部分,即可以使用,也可以直接忽略。
注意,导入包的同时,会在包目录下生成一个含有 init.cpython-36.pyc 文件的 pycache 文件夹。
# 3. 查看模块(变量、函数、类)方法
# 3.1 查看模块成员:dir()函数
通过 dir() 函数,我们可以查看某指定模块包含的全部成员(包括变量、函数和类)。注意这里所指的全部成员,不仅包含可供我们调用的模块成员,还包含所有名称以双下划线“__”开头和结尾的成员,而这些“特殊”命名的成员,是为了在本模块中使用的,并不希望被其它文件调用。
这里以导入 string 模块为例,string 模块包含操作字符串相关的大量方法,下面通过 dir() 函数查看该模块中包含哪些成员:
import string
print(dir(string))
['Formatter', 'Template', '_ChainMap', '_TemplateMetaclass', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_re', '_string', 'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'capwords', 'digits', 'hexdigits', 'octdigits', 'printable', 'punctuation', 'whitespace']
2
3
4
显然通过列表推导式,可在 dir() 函数输出结果的基础上,筛选出对我们有用的成员并显示出来。
import string
print([e for e in dir(string) if not e.startswith('_')])
['Formatter', 'Template', 'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'capwords', 'digits', 'hexdigits', 'octdigits', 'printable', 'punctuation', 'whitespace']
2
3
4
# 3.2 查看模块成员:__all__变量
除了使用 dir() 函数之外,还可以使用 all 变量,借助该变量也可以查看模块(包)内包含的所有成员。
import string
print(string.__all__)
['ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'capwords', 'digits', 'hexdigits', 'octdigits', 'printable', 'punctuation', 'whitespace', 'Formatter', 'Template']
2
3
4
和 dir() 函数相比,all 变量在查看指定模块成员时,它不会显示模块中的特殊成员,同时还会根据成员的名称进行排序显示。
不过需要注意的是,并非所有的模块都支持使用 all 变量,因此对于获取有些模块的成员,就只能使用 dir() 函数。
# 3.3 查看文档:__doc__属性
无论是函数还是类,都可以使用 doc 属性获取它们的说明文档,模块也不例外。
以 my_package 包 module1 模块中的 display() 函数为例,我们尝试用 doc 变量获取其说明文档:
import my_package
print(my_package.module1.display.__doc__)
2
那么,如果使用 help() 函数或者 doc 属性,仍然无法满足我们的需求,还可以使用以下 2 种方法:
- 调用 file 属性,查看该模块或者包文件的具体存储位置,直接查看其源代码(后续章节或详细介绍);
- 对于非自定义的模块或者包,可以查阅 Python (opens new window) 库的参考文档 https://docs.python.org/3/library/index.html。
# 3.4 查看模块的源文件路径:__file__属性
当指定模块(或包)没有说明文档时,仅通过 help() 函数或者 doc 属性,无法有效帮助我们理解该模块(包)的具体功能。在这种情况下,我们可以通过 file 属性查找该模块(或包)文件所在的具体存储位置,直接查看其源代码。
import my_package
print(my_package.__file__)
# C:\\Users\\mengma\\Desktop\\my_package\\__init__.py
2
3
4
由此,通过调用 file 属性输出的绝对路径,我们可以很轻易地找到该模块(或包)的源文件。
注意,并不是所有模块都提供 file 属性,因为并不是所有模块的实现都采用 Python 语言,有些模块采用的是其它编程语言(如 C 语言)。