模块就是完成某项功能的程序集,比如 sys
模块,random
模块等。在 Python 语言中,每个 .py
文件就是一个模块。
创建一个模块
任何以 .py
结尾的文件都是一个模块。
创建 tool.py 文件:
def greeting(name):
print("Hello, I'm %s"%name)
def add(a,b):
return a+b
在 py.py 中使用该模块:
import tool
tool.greeting("Mike")
运行结果:
Hello, I'm Mike
引入模块的几种方式
以上面创建的 tool 模块为例,有一下几种引入方式(1 为引入方式,2 为调用方式):
1.import tool
2.tool.greeting("Mike")
1.from tool import greeting,add
2.greeting("Mike")
1.from tool import *
2.greeting("Mike")
__all__ 变量
默认情况下,使用 from xxx import *
导入模块时会导入模块中所有的函数和数据,如果我们想限定哪些内容能被导入,哪些内容不能被导入,就需要使用 __all__
变量,该变量是系统内置的,默认为一个空列表。我们可以在该列表中添加想要被导出的内容。修改 tool.py:
__all__ = ["greeting"]
def greeting(name):
print("Hello, I'm %s"%name)
def add(a,b):
return a+b
在 py.py 中调用 add
方法试试:
from tool import *
greeting("Mike")
print(add(1,2))
运行结果如下:
Hello, I'm Mike
Traceback (most recent call last):
File "C:\Users\Charley\Desktop\py\py.py", line 4, in <module>
print(add(1,2))
NameError: name 'add' is not defined
greeting
函数被成功执行了,而 add
函数没有找到。
对于模块内部的私有变量或者函数,还可以在名称前加上一个下划线 _
,这样就不会被导出了。
注:这种方式只对 from xxx import *
有效:
tool.py:
_num = 1
def count():
global _num
_num += 1
return _num
py.py:
from tool import *
print(count())
print(_num)
运行结果如下:
2
Traceback (most recent call last):
File "C:\Users\Charley\Desktop\py\py.py", line 3, in <module>
print(_num)
NameError: name '_num' is not defined
[Finished in 0.1s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]
这里无法使用 _num
变量。以上的方式只对 from xxx import *
有效,如果我们不使用星号 *
引入,则无此限制:
from tool import count,_num
print(count())
print(_num)
运行结果为:
2
1
__pycache__ 文件夹
在导入 tool 模块后运行代码,发现多出了一个 __pycache__
文件夹:
│ py.py
│ tool.py
│
└─__pycache__
tool.cpython-35.pyc
其中有个 .pyc
文件,这是编译器为了提高运行速度创建的缓存字节码文件,这里有一个简要介绍:
如果 Python 进程在机器上拥有写入权限,那么它将把程序的字节码保存为一个以 .pyc 为扩展名的文件( ".pyc" 就是编译过的 ".py" 源代码)。当程序运行之后,你会在那些源代码的附近(也就是说同一个目录下)看到这些文件。
Python这样保存字节码是作为一种启动速度的优化。下一次运行程序时,如果你在上次保存字节码之后没有修改过源代码的话,Python将会加载.pyc文件并跳过编译这个步骤。当Python必须重编译时,它会自动检查源文件和字节码文件的时间戳:如果你又保存了源代码,下次程序运行时,字节码将自动重新创建。
上面的解释来自于《python学习手册》。
总是,__pycache__
和其下的 .pyc
文件是解释器为了优化代码执行创建的,并且只会创建被导入的模块缓存,主模块不创建缓存。
包
有时候一个模块无法完成具体的功能,需要多个模块协作完成,我们可以把这些模块放在一个文件夹中进行统一管理,这就是 Python 中包的概念。
Python3 和 Python2 对于包的概念有点小小的区别:Python3 认为一个文件夹就是一个包,Python2 认为带有 __init__.py
文件的文件夹是一个包。但不管是 Python3 还是 Python3,如果包中有 __init__
文件,在导入包的时候都会先执行 __init.py__
文件。
Python3 下可以直接导入包中的模块:
from mypkg import tool
tool.greeting("NAME")
运行结果:
Hello, I'm NAME
Python2 下需要新建 __init__.py
文件:
__all__ = ["tool"]
除此之外使用方式和 Python3 一样:
from mypkg import tool
tool.greeting("Python2 中使用")
为了兼容性,建议总是在包中加上 __init__.py
文件。
模块寻找的路径
sys
模块中的 path
变量可以帮助我们查看 Python 寻找模块的路径:
import sys
print(sys.path)
运行结果:
[
'C:\\Users\\Charley\\Desktop\\py',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages'
]
在寻找模块时,会依次按照列表中的路径寻找,直到找到位置。
如果我们想自定义寻找路径,可以修改 path
变量:
from sys import path
path.append("./mypkg/");
from tool import greeting
greeting("Mike")
运行结果为:
Hello, I'm Mike
[
'C:\\Users\\Charley\\Desktop\\py',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32',
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages',
'./mypkg/'
]
我们在 path
变量中增加了一条路径,定义到前面建立的包中,这样就可以直接导入包中的模块,不需要导入包名了。这只是一种展示性的做法,不推荐使用。
模块热导入
这是我自己编的一个名词,意思是导入模块后,程序在不退出的前提下,如果修改了被导入的模块,可以同步使用修改后的模块中的功能。
创建一个 tesereload.py 模块:
def show():
print("Hello World")
# print("Hello China")
打开命令行,导入 testreload 模块,并执行 show
函数:
>>> import testreload
>>> testreload.show()
Hello World
修改 testreload 模块,取消注释,再次在命令行中执行 show
函数:
>>> testreload.show()
Hello World
在命令行中使用 import
再次导入 testreload 模块,再执行 show
函数:
>>> import testreload
>>> testreload.show()
Hello World
执行结果并没有发生变化,这是因为前面讲到的缓存机制,在程序运行起来之后,修改了原始模块并不会同步更新导入,而是从缓存的 pyc
文件中读取。如果需要重新应用修改后的模块,可以:
- 重新启动程序
- 使用
imp
模块中的reload
方法
继续在命令行中导入 imp
模块,并通过 reload
方法重新载入模块:
>>> from imp import reload
>>> reload(testreload)
<module 'testreload' from 'C:\\Users\\Charley\\Desktop\\py\\testreload.py'>
再次执行 show
方法:
>>> testreload.show()
Hello World
Hello China
可见 show
方法的执行结果已经同步更新了。这就是 reload
的作用。
避免模块循环导入
模块循环导入就是两个模块之间相互依赖导入,会产生死循环,修改 py.py 文件:
from testreload import show
def show2():
pass
show()
修改 testreload.py 文件:
from py import show2
def show():
print("Hello World")
show2()
运行 py.py 文件:
Traceback (most recent call last):
File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
from testreload import show
File "C:\Users\Charley\Desktop\py\testreload.py", line 1, in <module>
from py import show2
File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
from testreload import show
ImportError: cannot import name 'show'
[Finished in 0.2s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]
[dir: C:\Users\Charley\Desktop\py]
[path: C:\Python27\;C:\Python27\Scripts;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\nvm;D:\nodejs;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramData\chocolatey\bin;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\Scripts\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\;D:\Git\bin;D:\Sublime Text 3;D:\MinGW\bin\;D:\MinGW\bin\;D:\Microsoft VS Code\bin;D:\Java\jdk 8.0\bin;D:\Android\sdk;C:\Program Files\MySQL\MySQL Server 5.7\bin;]
程序直接挂掉了。因此我们需要避免模块的循环导入,若两个模块相互有依赖,应该通过第三个的抽象进行解决。
__name__ 变量
__name__
变量是系统提供的一个全局变量,用来对当前的模块进行描述。我们在 py.py 文件和 testreload.py 文件中分别查看 __name__
变量:
py.py:
import testreload
print("--> py %s"%__name__)
testreload.py:
print("--> testreload %s"%__name__)
运行 py.py 文件:
--> testreload testreload
--> py __main__
__name__
变量用来对当前的模块进行描述,如果是主模块,其值为 __main__
,如果是被导入的模块,其值就是模块名。
实际应用中,我们可以根据 __name__
变量来决定是否执行模块中的内容。修改 testreload.py:
print("--> testreload %s"%__name__)
if __name__ == "__main__":
print("__MAIN__")
运行 py.py 文件:
--> testreload testreload
--> py __main__
运行 testreload.py 文件:
--> testreload __main__
__MAIN__
在 testreload 模块中,只有该模块是组模块中才执行 print
函数输出。
项目中我们一般有一个入口文件,建议在入口文件中对 __name__
进行判断,完成初始化工作:
# 如果当前模块是主模块
if __name__ == "__main__":
# 执行初始化函数
main()
完。