python基础教程:Python模块(module)

在此之前,我们运行一些简短的Python代码,都是通过Python解释器(python或ipython)进行的,如果我们退出解释器再重新运行解释器后,上次敲进去的变量、函数等都不见了,没有保存下来。为了保存我们曾经写过的代码,就是要写成.py文件,称为脚本

如果你这个脚本想要使用其它脚本里面的变量、函数和类也是可以的,在你这个脚本里面用import来导入要引用的脚本,而那个被引用的脚本就称作模块(module)

python基础教程:Python模块(module)

简单来说,一个Python源码文件(*.py)就是一个模块。

我们的第一个Python模块

接下来,我们用文本编辑器(比如,前面介绍的VS Code)来创建一个名为 my_first_module.py的文件作为我们编写的第一个模块:

#!/usr/bin/env python3
# coding:utf-8
# Author: veelion
# file name: my_first_module.py

'''My First Module'''

MY_NAME = 'My_First_Module'

def my_print(a):
    print(MY_NAME, ' print', a)

def my_add(a, b):
    return a+b

我们的第一个Python模块里面有一个全局变量:MY_NAME,两个函数:my_print()my_add()。接着我们在这个文件所在目录运行Python解释器ipython:

In [1]: import my_first_module

In [2]: my_first_module?
Type:        module
String form: <module 'my_first_module' from '/home/veelion/p2/tutorial/md_Python/codes/my_first_module.py'>
File:        ~/p2/tutorial/md_Python/codes/my_first_module.py
Docstring:   My First Module

In [3]: my_first_module.MY_NAME
Out[3]: 'My_First_Module'

In [4]: my_first_module.my_add(2, 3)
Out[4]: 5

In [5]: my_first_module.my_print('猿人学')
My_First_Module  print 猿人学

导入模块用import,模块名称就是文件名my_first_module.py去掉文件后缀.py后的名字。从上面ipython的使用中,我们可以看到模块中的函数、变量都是可以被拿来用的。

注意: Python模块的文件名只能是字母、数字和下划线,不能有-,+等其它符号,否则导入会报错,原因很简单,比如-符号会和Python里面的减号混淆。

把上面的模块重命名为my-first-module.py,再import导入一下看看:

In [6]: import my-first-module
  File "<ipython-input-6-a8306ca40c5e>", line 1
    import my-first-module
             ^
SyntaxError: invalid syntax

Python模块的二三事

(1)模块可以包含可执行的全局语句。这些语句应该是用于初始化该模块,它们只在第一次被import时执行。我们来举个例子,创建两个只包含一句print的模块:

# m1.py
print('m1 is imported')
# m2.py
import m1
print('m2 is imported')

main.py中导入m1m2这两个模块:

import m1
import m2
import m1

print('I am main.py')

这里m1被显性导入两次,还有一次被m2阴性导入,一共导入三次,那么是不是m1 is imported会被打印3次呢?我们运行这个脚本试试看: python main.py。猜猜运行结果是怎样的?

m1 is imported
m2 is imported
I am main.py

结果是只被打印了一次。这就是只在第一次被import时执行的意思。再试试把main.py中的两个import m1都去掉,只import m2会是什么结果?

(2)每个模块都都它自己私有的符号表,它被当做全局符号表被该模块中所有函数使用,也就是说,每个模块都有自己的名字空间。因此,模块里面可以尽情(如有必要)使用全局变量,而不用担心它们与模块使用者的全局变量冲突。用户使用模块中的全局变量也很简单:modname.itemname
比如,my_first_module模块中的MY_NAME使用时就是my_first_module.MY_NAME,而在你自己的脚本里面同样可以命名MY_NAME的全局变量,而不会和my_first_module里面的冲突。

(3)模块可以import其它模块。模块导入语句import不一定要在脚本的最开始,可以在代码其它位置需要时导入。当然,在最开始导入是最清晰、规范的做法。

import 模块的各种方式

我们使用import的方式很多,前面那种 import module_name的方式是最常用的,也是代码规范推崇的用法。从语法上讲还有其它方式:

(1)用from导入部分:

In [1]: from my_first_module import my_add

In [2]: my_add(1,3)
Out[2]: 4

In [3]: my_print('hi')
---------------------------------------------------------------------------
NameError         Traceback (most recent call last)
<ipython-input-3-b42bb20df9e4> in <module>
----> 1 my_print('hi')

NameError: name 'my_print' is not defined

In [4]: my_first_module.my_add(1,2)
---------------------------------------------------------------------------
NameError         Traceback (most recent call last)
<ipython-input-4-df5ce230b443> in <module>
----> 1 my_first_module.my_add(1,2)

NameError: name 'my_first_module' is not defined

通过from modname import xxx的方式,我们只导入了my_add,调用my_print就会出错。并且,my_first_module模块名称也是未定义的,即没有被导入。

(2)用from导入部分并重命名
跟(1)一样,只不过把导入的名称起了别名而已,使用时用别名:

from my_first_module import my_add as myadd

(3)用from导入全部

In [1]: from my_first_module import *

In [2]: my_add(1,2)
Out[2]: 3

In [3]: my_print('猿人学')
My_First_Module  print 猿人学

In [4]: MY_NAME
Out[4]: 'My_First_Module'

这种方式看似简单,写代码时省去了模块名称my_first_module这个前缀。但是,这个省略带来很大隐患,会限制我们自己命名。如果我们自己命名和模块里面的名称一样,就会覆盖模块里面的名字。

这种import的方式是代码规范严禁杜绝的

(4)重命名模块
如果模块名称很长,我们可以给它起个短一点的别名,这样写代码会简单些:

In [1]: import my_first_module as mfm

In [2]: mfm.my_add(1,2)
Out[2]: 3

In [3]: mfm.my_print('猿人学')
My_First_Module  print 猿人学

In [4]: mfm.MY_NAME
Out[4]: 'My_First_Module'

这个和import my_first_module实际上一样,只是使用的名称变为mfm。模块的别名可以任意起,只要和其它名称区分开来就好。

(5)模块重新加载
我们写完一个模块,可能要通过Python解释器(如ipython)进行验证一下,于是运行Python解释器,import模块,发现模块的某个函数有错误,就在编辑器修改了该函数并保存该模块文件。继续在刚才打开的解释器里面验证那个有错误的函数,发现刚才的修改没生效,竟然没有生效!!!

为什么呢?为了效率,每个解释器导入的模块只导入一次。因此,如果你中途修改了模块,就要出解释器重新进入并重新导入模块才能使修改生效。如果不退出解释器而重新导入模块,不管你运行多少次import modname都是无效的,因为解释器一看这个模块已经导入过了,就不费劲再导入一次了。解释器懒,你就不能懒。

或者,可以不重新启动解释器而使用importlib.reload()重新导入。

把Python模块当做脚本运行

任何Python文件都可以这样来运行:

python3 file.py

一个文件的Python模块当然也可以这样运行。一个Python文件,前面是函数的定义,定义完要运行,我们就要写调用语句,最初你相到的可能是这样的:

# Author: veelion
# file: mylib.py

def add(a, b):
    return a+b

print(add(2, 3))

通过python mylib.py运行一下,就可以得到运行结果。

目前看起来一切正常,你看看有没有问题?

回头看看上面模块二三事的第(1)条,如果这个文件当做模块被其它文件import时,是不是也会运行打印语句?这条打印语句往往是我们为了验证add()函数而进行的,属于测试阶段的代码,而交付给他人作为模块使用时,它是不需要的。那么,该怎么办?

通过__name__属性就可以来限制print(add(2, 3))语句的运行。文件作为脚本运行时,它的__name__属性是__main__,而作为模块被import时,它的__name__属性是模块的名称。

先看看模块被import时的__name__

In [24]: import mylib
5

In [25]: mylib.__name__
Out[25]: 'mylib'

我们可以看到,import mylib后打印出了5,也就是运行了print(add(2, 3))语句。

然后,我们修改mylib.py文件,把测试语句修改一下:

# Author: veelion
# file: mylib.py

def add(a, b):
    return a+b

if __name__ == '__main__':
    print(add(2, 3))

再次在ipython解释器里面导入该模块时就不会打印出5,也就是那句print不再执行。
而在命令行下运行python3 mylib.py这个脚本就会执行那句print语句,因为这种执行方式下,模块的__name____main__

这些用__name__ == '__main__'条件判断的代码通常是该模块的测试代码,或者是如何使用该模块的示例代码。

Python模块总结

(1)一个Python文件就是一个模块;
(2)一个模块可以import其它模块;
(3)在Python解释器运行中,一个模块只可以被import一次,除非使用importlib.reload();
(4)模块中的可执行语句(非函数、类的定义)仅在该模块被import时执行一次。
(5)import模块的方式有多种,要使用最规范的方式。

相关推荐