模块

模块就是完成某项功能的程序集,比如 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()

完。

    原文作者:Charleylla
    原文地址: https://www.jianshu.com/p/5f98fa52e5ad
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞