Python 模块的使用

Python 模块的使用

模块介绍

什么是模块?

模块就是一组功能的集合体,我们的程序可以导入模块来复用模块里的功能。
模块分为三大类:

  1. 自定义模块
  2. 内置模块:time, sys
  3. 第三方模块

常见的场景
一个模块就是一个包含了一组功能的 python 文件,比如 spam.py,模块名为 spam,可以通过 import spam 使用。

模块四种表现形式
在 Python 中,模块的使用方式都是一样的,但其实细说的话,模块可以分为四个通用类别

  1. 使用 Python 编写的 .py 文件
  2. 已被编译为共享库或 DLL 的 C 或 C++ 扩展
  3. 把一系列模块组织到一起的文件夹(注:文件夹下有一个 __init__.py 文件,该文件夹称之为包)
  4. 使用 C 编写并链接到 python 解释器的内置模块
1
2
3
4
5
6
7
8
import time
print(time)
import sys
print(sys)

# 执行结果
<module 'time' (built-in)>
<module 'sys' (built-in)>

为什么要用模块?

  1. 可以将程序中频繁使用的一些公共功能
    可以拿内置的、第三方的模块,然后直接使用,这种拿来主义,可以极大地提高开发效率
  2. 将程序中公用的一些功能组织到一个文件中,然后程序各部分组件可以重用该文件中的功能

优点:减少代码冗余,增强程序的组织结构性与可维护性

如何使用模块?

1537516081466

被导入的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vim spam.py
print('from the spam.py')

money = 1000

def read1():
print('spam模块:', money)

def read2():
print('spam模块')
read1()

def change():
global money
money = 0

执行文件

1
2
3
4
5
6
vim run.py

import spam

# 执行结果
from the spam.py

导入模块:import 模块名
首次导入模块都发生了哪些事?

  1. 先产生一个模块的名称空间
  2. 会执行模块文件的代码,将产生的名字放到模块的名称空间中
  3. 会在当前名称空间中拿到一个模块名,该模块名指向模块的名称空间

import导入模块配图

示例1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 如何使用模块?
import spam

# 模块名.名字,是在向模块的名称空间中拿名字
print(spam.money)
print(spam.read1)
print(spam.read2)
print(spam.change)

# 执行结果
from the spam.py
1000
<function read1 at 0x000002629B29B6A8>
<function read2 at 0x000002629B29B620>
<function change at 0x000002629B29B840>
1
2
3
4
5
6
7
8
import spam

money = 0
print(spam.money)

# 执行结果
from the spam.py
1000

示例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vim spam.py
# print('from the spam.py')

money = 1000

def read1():
print('spam模块:', money)

def read2():
print('spam模块')
read1()

def change():
global money
money = 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import spam

spam.read1()
# 执行结果
spam模块: 1000

############################################################################
import spam

# 凡是来自于 spam 名称空间中的功能,执行时都是模块自己的名称空间为准的
money = 0
spam.read1()
# 执行结果
spam模块: 1000

示例3

观察 spam.py 里的 change()

1
2
3
4
5
import spam

money = 1111111111111111111111
spam.change()
print(money)
1
2
3
4
5
6
7
8
import spam

money = 1111111111111111111111
spam.change()
spam.read1()

# 执行结果
spam模块: 0

示例4

1
2
3
4
5
6
7
8
9
10
11
12
import spam

money = 1111111111111111111111

def read1():
print('run.py read1')

spam.read2()

# 执行结果
spam模块
spam模块: 1000

import 导入模块的方式,在引用模块名称空间中改名字时,必须加上前缀:模块名.名字
优点:指定前缀地访问模块名称空间中的名字,不会与当前名称空间中名字冲突
缺点:每次引用模块名称空间中的名字都需要加上前缀,模块名过长时,前缀会显得非常臃肿

起别名可以缩短 模块名.名字 的长度

1
2
3
4
# 起别名方式
import spam as sm

print(sm.money)

模块用逗号分隔,一次导入多个模块

1
import spam, os, time

推荐多行导入

1
2
3
import spam
import os
import time

from...import... 导入模块

可以在调用模块名称空间中的名字时,不需要在名字前加前缀模块名

1
2
3
4
5
6
x = 1
y = 2
from spam import money

# 执行结果
from the spam.py

from...import...首次导入模块都发生了哪些事?

  1. 先产生一个模块的名称空间
  2. 会执行模块文件的代码,将产生的名字放到模块的名称空间中
  3. 会在当前名称空间中直接拿到一个模块名称空间中的名字

from-import导入模块20180922185501

1
2
3
4
5
6
7
8
x = 1
y = 2
from spam import money

print(money)

# 执行结果
1000
1
2
3
4
5
6
7
8
9
10
11
12
13
x = 1
y = 2
from spam import money, read1, read2

# 使用:可以不用加前缀直接使用
print(money)
print(read1)
print(read2)

# 执行结果
1000
<function read1 at 0x000002384886B6A8>
<function read2 at 0x000002384886B620>

使用:可以不用加前缀直接使用
优点:简洁
缺点:容易与当前名称空间中的名字冲突

from-import导入模块20180922185516

强调:来自于模块名称空间中的函数一定是模块的名称空间为准的

1
2
3
4
5
6
7
8
9
10
x = 1
y = 2
from spam import money, read1, read2, change

money = 1
change()
print(money)

# 执行结果
1
1
2
3
4
5
6
7
8
9
10
x = 1
y = 2
from spam import money, read1, read2, change

money = 1
change()
read1()

# 执行结果
spam模块: 0

模块名称空间里的名字可以不用一个个导入
但是不推荐使用 '*',因为不知道会导入哪些功能名字,最好需要哪一个导入哪一个,这样比较清楚

1
2
3
4
5
6
7
8
9
10
11
12
from spam import *

print(money)
print(read1)
print(read2)
print(change)

# 执行结果
1000
<function read1 at 0x000001F14E00B6A8>
<function read2 at 0x000001F14E00B620>
<function change at 0x000001F14E00B840>

起别名

1
2
3
4
5
from spam import money as m
print(m)

# 执行结果
1000

控制 '*' 所导入的名字

1
2
3
vim spam.py
__all__ = ['money', 'read1'] # 控制的就是 '*' 所导入的名字
...省略...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from spam import *

print(money, read1)

# 执行结果
1000 <function read1 at 0x000002035A6DB8C8>

###############################################################################
from spam import *

print(money, read1)
print(read2)

# 执行结果
NameError: name 'read2' is not defined

模块的嵌套(循环)导入

在 Python 中,一个模块凡是被导入过一次,下一次导入就会直接引用上一次导入的结果,即不会重新执行模块的文件

1537626500840

1
2
3
4
vim m1.py
print('正在导入m1')
from m2 import y
x = 'm1'
1
2
3
4
vim m2.py
print('正在导入m2')
from m1 import x
y = 'm2'
1
2
3
4
5
6
7
8
9
10
11
vim run.py
import m1

# 执行结果
正在导入m1
Traceback (most recent call last):
正在导入m2
import m1
from m2 import y
from m1 import x
ImportError: cannot import name 'x' from 'm1'

以上代码执行过程

  1. 先运行 run.py,产生 run.py 的名称空间,开始运行 run.py 里的代码。
  2. 运行到 import m1 这行代码时,会产生一个 m1 的名称空间,开始运行 m1.py 里的代码。
  3. 运行到 from m2 import y 会发现需要导入 m2,会产生一个 m2 的名称空间,开始运行 m2.py 里的代码,但是 m1.py 里的代码 还没有完全运行完
  4. m2.py 里需要 from m1 import x 导入 m1 里的 x,需要导入 m1
  5. 此时会产生一个问题,m1 已经被导入过了,此时不会再执行 m1 里的代码了,它只会直接引用 m1 的名称空间,直接向这个名称空间寻求 x,然而 m1 的名称空间中并没有 x

解决方法1

1
2
3
4
vim m1.py
print('正在导入m1')
x = 'm1'
from m2 import y
1
2
3
4
vim m2.py
print('正在导入m2')
y = 'm2'
from m1 import x
1
2
3
4
5
6
vim run.py
import m1

# 执行结果
正在导入m1
正在导入m2

解决方法:
把名字放在导入模块代码的上面,保证在导入之前先产生名字

  1. 先运行 run.py,产生 run.py 的名称空间,开始运行 run.py 里的代码。
  2. 运行到 import m1 代码,产生一个 m1 的名称空间,开始运行 m1.py 里的代码,把 x='m1' 放进去
  3. 运行到 from m2 import y 代码,需要导入 m2,产生一个 m2 的名称空间
    ,开始运行 m2.py 里的代码,造一个 y='m2',然后运行到 from m1 import x 代码,需要导入 m1m1 名称空间已经存在,寻求 x,此时寻找到了 x

1537632295691

解决方法2(推荐)

写代码尽量避免循环导入
如果必须面对这种场景,可以使用第三个模块把共享的模块做统一导入

解决方法3

  • 示例一
1
2
3
4
5
6
7
8
vim m1.py
print('正在导入m1')

def f1():
from m2 import y
print(y)

x = 'm1'
1
2
3
4
5
6
7
8
vim m2.py
print('正在导入m2')

def f2():
from m1 import x
print(x)

y = 'm2'
1
2
3
4
5
6
7
8
9
vim run.py

import m1
m1.f1()

# 执行结果
正在导入m1
正在导入m2
m2

解决思路
定义函数时,只检测语法,不执行代码
在哪个函数中使用,就在哪个函数当中去 import 导入
运行 run.py,产生 run.py 的名称空间,开始运行 run.py 里的代码。
运行到 import m1 代码,产生一个 m1 的名称空间,开始运行 m1.py 里的代码,把 f1x='m1' 放进去
run.py,里执行 m1.f1(),调用 m1f1 方法,导入 m2,产生 m2 的名称空间
导入 m2 后,运行 m2.py 的代码,m2 里把 f2y='m2' 放进去

1537633906337

  • 示例二
1
2
3
4
5
6
7
8
vim m1.py
print('正在导入m1')

def f1():
from m2 import y, f2
f2()

x = 'm1'
1
2
3
4
5
6
7
8
vim m2.py
print('正在导入m2')

def f2():
from m1 import x
print(x)

y = 'm2'
1
2
3
4
5
6
7
8
9
vim run.py

import m1
m1.f1()

# 执行结果
正在导入m1
正在导入m2
m1

区分Python文件两种用途方式

  1. 直接运行,当做运行文件
  2. 被当做模块导入使用

需求:
在文件运行的时候执行一种代码
被当做模块导入的时候运行另一种代码

当文件被直接执行时 __name__ == '__main__'

1
2
3
4
5
vim m1.py
print(__name__)

# 执行结果
__main__

当文件被导入时 __name__ == '模块名'
模块的使用者

1
2
3
4
import m1

# 执行结果
m1

模块开发者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vim m1.py
def f1():
print('f1')

def f2():
print('f2')

def f3():
print('f3')

if __name__ == '__main__':
f1()
f2()
f3()

# 执行结果
f1
f2
f3

该行代码用于区分 Python 文件的两种不同用途,应该写在文件末尾

模块的使用者

1
2
3
4
5
6
import m1

m1.f1() # 使用哪个模块就调用哪个

# 执行结果
f1

模块的搜索路径

模块的查找有限顺序:

  1. 内存中已经加载的模块
  2. 内置模块
  3. sys.path 路径中包含的模块

内存中已经加载的模块

模块的搜索路径配图1

操作方式:在 20秒内 把 spam.py 文件删除

1
2
3
vim spam.py
def f1():
print('from f1')

1
2
3
4
5
6
7
8
9
10
vim run.py
import time
import spam

time.sleep(20)
import spam
spam.f1()

# 执行结果
from f1

发现 还是能输出 from f1,说明在删除文件之前,内存已经加载到了 spam.py

内置模块

1
2
3
4
5
import time
print(time)

# 执行结果
<module 'time' (built-in)>

PS:我们自定义的模块名不应该与系统内置模块重名。

sys.path 路径中包含的模块

sys.path 的值是以当前执行文件为准

1
2
3
4
5
import sys
print(sys.path)

# 执行结果
['E:\\PycharmProjects\\SH_weekend_s1\\day05\\08 模块的使用\\模块的搜索路径', 'E:\\PycharmProjects\\SH_weekend_s1', 'D:\\Program Files (x86)\\Python37\\python37.zip', 'D:\\Program Files (x86)\\Python37\\DLLs', 'D:\\Program Files (x86)\\Python37\\lib', 'D:\\Program Files (x86)\\Python37', 'D:\\Program Files (x86)\\Python37\\lib\\site-packages', 'D:\\Program Files (x86)\\JetBrains\\PyCharm 2018.2.3\\helpers\\pycharm_matplotlib_backend']

示例
模块的搜索路径配图2

1
2
3
vim spam.py
def f1():
print('from spam')
1
2
3
4
5
6
7
8
9
10
vim run.py
import sys

sys.path.append(r'E:\PycharmProjects\SH_weekend_s1\day05\08 模块的使用\模块的搜索路径\dir')

import spam
spam.f1()

# 执行结果
from spam

也可以在当前目录寻找 from ... import ...

1
2
3
4
5
6
7
8
9
import sys
print(sys.path)

from dir import spam
spam.f1()

# 执行结果
['E:\\PycharmProjects\\SH_weekend_s1\\day05\\08 模块的使用\\模块的搜索路径', 'E:\\PycharmProjects\\SH_weekend_s1', 'D:\\Program Files (x86)\\Python37\\python37.zip', 'D:\\Program Files (x86)\\Python37\\DLLs', 'D:\\Program Files (x86)\\Python37\\lib', 'D:\\Program Files (x86)\\Python37', 'D:\\Program Files (x86)\\Python37\\lib\\site-packages', 'D:\\Program Files (x86)\\JetBrains\\PyCharm 2018.2.3\\helpers\\pycharm_matplotlib_backend']
from spam

示例
模块的搜索路径配图3

1
2
3
4
5
6
7
8
9
import sys
print(sys.path)

from dir.dir2 import spam
spam.f1()

# 执行结果
['E:\\PycharmProjects\\SH_weekend_s1\\day05\\08 模块的使用\\模块的搜索路径', 'E:\\PycharmProjects\\SH_weekend_s1', 'D:\\Program Files (x86)\\Python37\\python37.zip', 'D:\\Program Files (x86)\\Python37\\DLLs', 'D:\\Program Files (x86)\\Python37\\lib', 'D:\\Program Files (x86)\\Python37', 'D:\\Program Files (x86)\\Python37\\lib\\site-packages', 'D:\\Program Files (x86)\\JetBrains\\PyCharm 2018.2.3\\helpers\\pycharm_matplotlib_backend']
from spam

示例
模块的搜索路径配图4

1
2
3
4
5
6
vim m1.py
import m2

def f1():
print('m1.f1')
m2.f2()
1
2
3
vim m2.py
def f2():
print('m2.f2')
1
2
3
4
5
6
7
8
vim run.py
from dir1 import m1
m1.f1()

# 执行结果
from dir1 import m1
import m2
ModuleNotFoundError: No module named 'm2'

示例修改1

1
2
3
4
5
vim m1.py
from dir1 import m2
def f1():
print('m1.f1')
m2.f2()
1
2
3
vim m2.py
def f2():
print('m2.f2')
1
2
3
4
5
6
7
vim run.py
from dir1 import m1
m1.f1()

# 执行结果
m1.f1
m2.f2

示例修改2

1
2
3
4
5
vim m1.py
from . import m2
def f1():
print('m1.f1')
m2.f2()
1
2
3
vim m2.py
def f2():
print('m2.f2')
1
2
3
4
5
6
7
vim run.py
from dir1 import m1
m1.f1()

# 执行结果
m1.f1
m2.f2
---------------- The End ----------------
0%