目录

一、模块的基本用法

一、导入模块

二、编写一个模块

二、在哪里查找模块

三、是否需要编译模块

四、模块也可独立运行

五、如何查看模块提供的函数名


Python中的模块实际上就是包含函数或者类的 Python 脚本。对于一个大型脚本而言,经常需要将其功能细化,将实现不同功能的代码放到不同的脚本中实现,在其他的脚本中以模块的形式使用细化的功能,以便于脚本的维护与重用。

一、模块的基本用法

模块是包含函数和其他语句的 Python 脚本文件,它以 ".py" 为后缀名,也就是 Python 脚本的后缀名。用做模块的 Python 脚本与其他脚本并没有什么区别。

在 Python 中可以通过导入模块,然后使用模块中提供的函数或者数据。

一、导入模块

在 Python 中可以使用以下两种方法导入模块或者模块中的函数。

  • import  模块名
  • import  模块名  as  新名字
  • from  模块名  import  函数名

其中,使用 import 是将整个模块导入,而使用 from 则是将模块中某一个函数或者名字导入,而不是整个模块。使用 import 和 from 导入模块还有一个不同:要想使用 import 导入模块中的函数,则必须以模块名 + "." + 函数名的形式调用函数;而要想使用 from 导入模块中的函数,则可以直接使用函数名调用,不必在前面加上模块名称。

>>> import math  # 使用 import 导入 math 模块
>>> math.sqrt(9)  # 使用 math 模块中的 sqrt 函数
3.0
>>> sqrt(9)  # 直接使用 sqrt 名字调用函数——发生错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sqrt' is not defined
>>> from math import sqrt  # 使用 from 导入 math 模块中的 sqrt 函数
>>> sqrt(9)  # # 直接使用 sqrt 名字调用函数——成功使用
3.0

 使用 from 导入模块中的函数后,使用模块中的函数会方便得多,不用在调用函数时使用模块名。如果要想将模块中所有函数都采用这种方式导入,则可以在 from 中使用 "*" 通配符,表示导入模块中的所有函数。

>>> from math import sqrt  # 仅从 math 模块中导入 sqrt
>>> sqrt(9)
3.0
>>> sin(30)  # 调用 math 模块中的 sin 函数出错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sin' is not defined
>>> from math import *  # 重新从 math 模块中导入所有函数
>>> sin(30)  # 重新调用 sin 函数
-0.9880316240928618

在 Python 2.x 中,还可以通过使用内置函数 reload 重新载入模块。reload 可以在模块被修改的情况下不必关闭 Python 而重新载入模块。在使用 reload 重载模块时,该模块必须已经事先被导入。

在 Python 3 中,reload 函数已经被删除,要重新载入模块,则需要使用 importlib(版本旧一点的名称是 imp ) 模块中的 reload 函数。

>>> import os              # 导入 os 模块
>>> import importlib       # 导入 importlib 函数
>>> importlib.reload(os)   # 重新载入 os 模块
<module 'os' from 'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\os.py'>

二、编写一个模块

下面是一个简单的模块,该模块中只包含一个函数。这个函数只打印 "I am a module!"。该模块保存为 mymodule.py。代码如下:

def show():   # 声明 show 函数
    print('I am a module!')

编写一个调用函数 show 的脚本,将其保存为 usemodule.py。代码如下:

import mymodule    # 导入模块
mymodule.show()    # 调用模块中的 show 函数

脚本运行后输出如下:

python module命名 python模块后缀名_python module命名

除了在模块中声明函数外,还可以在模块中定义变量。模块中的变量同样可以在其他脚本中使用。以下代码在 mymodule.py 模块中添加了一个名为 name 的变量。

def show():
    print('I am a module!')
name = 'mymodule.py'  # 在模块中添加一个 name 变量

以下代码为修改的 usemodule.py 模块,其中使用了 mymodule.py 中的 name 变量。

import mymodule
mymodule.show()
print(mymodule.name)      # 打印模块中的 name 变量
mymodule.name = 'usemodule.py'   # 将 name 变量重新赋值
print(mymodule.name)

脚本运行后输出如下:

python module命名 python模块后缀名_Python_02

二、在哪里查找模块

编写好的模块只有被 Python 找到才能被导入。上一节中编写的模块和调用模块的脚本位于同一个目录中,因此不需要进行设置就能被 Python 找到并导入。如果在该目录中新建一个 module 目录,并且把 mymodule.py 转移到 module 目录中,再次运行 usemodule.py,则输出如下:

python module命名 python模块后缀名_python_03

运行脚本出错,Python 解释器没有找到 mymodule.py 模块,在导入模块时,Python 解释器首先在当前目录中查找要导入的模块,如果未找到模块,则 Python 解释器会从 sys 模块中 path 变量指定的目录中查找导入模块。如果在以上所有目录中都未找到导入的模块,则会输出出错信息。

以下代码是使用 sys.path 输出 Python 的模块查找路径。

>>> import sys
>>> sys.path
['D:\\Python', 
'D:\\Python', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend']

在脚本中可以向 sys.path 添加模块查找路径。以下所示脚本中,将当前目录下的 module 子目录添加到 sys.path 中,并从 module 目录中导入 mymodule.py 模块。代码如下。

import os
import sys
modulepath = os.getcwd() + '\\module'
sys.path.append(modulepath)
print(sys.path)
import mymodule
mymodule.show()
print(mymodule.name)
mymodule.name = 'usenodule.py'
print(mymodule.name)

运行脚本输出如下:

['D:\\Python', 
'D:\\Python', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip',
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend', 
'D:\\Python\\module']
I am a module!
mymodule.py
usenodule.py

从输出可以看出,当前路径也被添加到了 sys.path 路径列表中,这说明 Python 其实是按照 sys.path 中的路径来查找模块的。之所以首先在当前目录中查找,那是因为 Python 解释器在运行脚本前将当前目录添加到 sys.path 路径列表中了。

三、是否需要编译模块

在上面的例子中,运行完 usemodule.py 会发现,module 目录中除了 mymodule.py 文件外,还多了一个后缀名为 ".pyc" 文件。其实,该文件就是 Python 将 mymodule.py 编译成字节码的文件。虽然 Python 是脚本语言,但 Python 可以将脚本编译成字节码的形式。对于模块,Python 总是在第一次调用后就将其编译成字节码的形式,以提高脚本的启动速度。

Python 在导入模块时会查找模块的字节码文件,如果存在则将编译后的模块的修改时间同模块的修改时间相比较。如果两者的修改时间不相符,则 Python 将重新编译模块,以保证两者内容相符。被编译的脚本也是可以直接运行的。

当然,没有必要去刻意编译 Python 脚本。不过,由于 Python 是脚本,如果不想将源文件发布,则可以发布编译后的脚本,这样能起到一定的保护源文件的作用。

对于非模块的脚本,Python 不会在运行脚本后将其编译成字节码的形式。如果想将其编译,可以使用 commile 模块。以下代码可以将 usemodule.py 文件编译成 ".pyc" 文件。

import py_compile       # 导入 py_compile 模块
py_compile.compile('usemodule.py','usemodule.pyc')   # 编译 usemodule.py

运行 compile.py 后,可以看到当前目录中多了一个 usemodule.pyc 文件。在 Python 3 中,如果在 py_compile.compile 函数中不指定第 2 个参数,则将在当前目录新建一个名为 "__pycache__" 的目录,并在这个目录中生成 "被编译模块名.cpython-32.pyc" 的 pytc 字节码文件。

运行 usemodule.py 脚本输出如下:

['D:\\Python', 
'D:\\Python', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip',
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32', 
'C:\\Users\\图图\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages', 
'C:\\Program Files\\JetBrains\\PyCharm 2018.3.5\\helpers\\pycharm_matplotlib_backend', 
'D:\\Python\\module']
I am a module!
mymodule.py
usenodule.py

可以看到其输出与前面的输出一样。编译后生成的 usemodule.pyc 文件并没有改变程序功能,只是以 Python 字节码的形式存在。

另外,还可以通过 Python 的命令行选项将脚本优化编译。Python 编译的优化选项有以下两个:

  • -O      该选项对脚本的优化不多,编译后的脚本以 ".pyo" 为扩展名。凡是以 ".pyo" 为扩展名的 Python 字节码都是经过优化的。
  • -OO   该选项对脚本优化的程度较大。使用该标志可以使编译的 Python 脚本更小。使用该选项可能导致脚本运行错误,因此,因谨慎使用。

可以通过在命令行中输入以下命令将 usemodule.py 优化编译。

python -O compile.py
python -OO compile.py

四、模块也可独立运行

每个 Python 脚本在运行时都有一个 __name__ 属性(name 前后均是两条下划线)。在脚本中通过对 __name__ 属性值的判断,可以让脚本在作为导入模块和独立运行时都可以正确运行。

在 Python 中,如果脚本作为模块被导入,则其 __name__ 属性被设置为模块名;如果脚本独立运行,则其 __name__ 属性被设置为 "__main__"。因此可以通过 __name__ 属性来判断脚本的运行状态。

以下所示脚本 mymodule2.py 既可以独立运行,也可以作为模块被其他脚本导入。

# file: mymodule2.py

def show():
    print('I am a module!')
if __name__ == '__main__':
    show()
    print('I am not a module!')

以下所示脚本 usemodule2.py 可以调用 mymodule2.py 模块

# file: usemodule2.py

import mymodule2
mymodule2.show()
print('my __name__ is', __name__)

运行 usemodule2.py 后,输出如下:

python module命名 python模块后缀名_python_04

运行 mymodule2.py 后,输出如下:

python module命名 python模块后缀名_python module命名_05

五、如何查看模块提供的函数名

如果需要获得导入模块中所有声明的名字、函数等,就可以使用内置的函数 dir() 来进行操作。

以下所示代码将获得 sys 模块中的名字和函数。

import sys
print(dir(sys))

输出结果如下:

['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', 
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', 
'__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', 
'_debugmallocstats', '_enablelegacywindowsfsencoding', '_framework', '_getframe', 
'_git', '_home', '_xoptions', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 
'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 
'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 
'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 
'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 
'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 
'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 
'path_importer_cache', 'platform', 'prefix', 'set_asyncgen_hooks', 
'set_coroutine_origin_tracking_depth', 'set_coroutine_wrapper', 'setcheckinterval', 
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 
'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']

dir() 函数的原型如下:

dir( [object] )
  • object        可选参数,要列举的模块名。

如果不向 dir() 函数传递参数,那么 dir() 函数将返回当前脚本的所有名字列表。如下所示。

a = [1, 3, 6]  # 定义一个列表
b = 'python'  # 定义一个字符串
print(dir())  # 使用 dir() 函数获得当前脚本所有名字列表


def fun():  # 定义一个函数
    print('python')


print(dir())  # 再次使用 dir() 函数

两个 print 的输出结果如下:

python module命名 python模块后缀名_字节码_06