模块的介绍
什么是模块?
模块就是一系列功能的集合体,模块分为三大类,分别是内置的模块、第三方模块、自定义模块。
所谓内置模块就是Python解释器自带的模块,我们直接拿来用即可。
第三方模块则是别人写好的Python文件,上传到Python模块网站上我们通过pip或者手动下载的方式,下载到的py文件。
而自定义模块,则是我们自己写的模块即py文件。
一个Python文件本身就是一个Python文件,比如func.py,那么该文件的模块名就为func
模块分为四种形式:
1、使用Python编写的.py文件
2、已被编译为共享库或DLL的C或C++扩展
3、把一系列模块组织到一起的文件夹(注意:文件夹下有__init__.py文件,该文件夹称之为包)
4、使用C编写链接到Python解释器的内置模块
其中2和4我们只做了解,和我们有关系的还是1和3。
为什么要用模块
1、内置模块与第三方模块拿来就用,无需自己编写或定义,这种拿来主义,可以大大提升我们的开发效率。而不low。
2、自定义模块可将程序的某些功能提取出来放到一个模块中,可以供大家使用,第三方模块就是这么来的。好处是减少代码的冗余,程序的整体架构较清晰,
如何导入模块
import time # 导入内置模块time
模块最大的作用就是,将一个Python文件的功能,引入到另外一个Python文件中去。
比如有如下的应用场景:
x1ong.py文件有如下代码:
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
print('helloworld')
你想让x1ong.py文件的功能,应用到test.py文件中去,如果没有模块的概念,那么你只能去将x1ong.py的代码复制给test.py去。
但是你有了调用模块之后,你只需要import一下即可。

首次导入模块发生的三件事
import x1ong # 导入一个自定义模块。
'''
首次导入模块发生的三件事:
1. 执行x1ong.py文件
2. 产生x1ong.py的名称空间,将x1ong.py运行过程中产生的名字都丢到x1ong的名称空间中
3. 在当前py文件中有个名字为x1ong,该名字执行第二件事产生的名称空间中
'''
print(x1ong) # <module 'x1ong' from '/Users/x1ong/Downloads/x1ong.py'>
x1ong.py文件的内容如下:
print('from x1ong moudel >>>>')
x = 10
def func():
print(x)
def change():
global x
x = 10000
import导入模块的基本使用
import x1ong
# 引用
print(x1ong.func)
print(x1ong.change)
print(x1ong.x)
'''
强调1: 模块名.名字,是指名道姓的问某一个模块要一个名字,不会与当前文件的名称空间发生任何冲突
'''
# 调用x1ong模块下的func函数
x1ong.func() # 10
# 调用x1ong模块下的change函数
x1ong.change() # 更改全局变量x的值为10000
'''
强调2: 无论是查看还是修改,都是以原模块的Python文件为基准的,与调用位置无关
'''
x = 123
x1ong.func() # 再次打印x 为 10000,可以看到并没有修改x的值为123,可见强调2总结
import导入模块的相关知识
1、可以在一行导入多个模块,以逗号分割即可。
# import sys
# import time
# import os
# 上述代码可以写为如下形式
import sys,time,os # 但是不推荐使用
2、导入模块的规范
- 先导入内置模块
- 再导入第三方模块
- 最后再导入自定义模块
'''
import time
import sys
import os
import 第三方模块1
import 第三方模块2
import 第三方模块3
import 自定义模块1
import 自定义模块2
'''
3、在import导入的时候为某个模块设置别名
import 模块名 as 别名
import time as t
# 如果为time设置了别名,则在以后调用time模块的时候。使用t.方法()调用即可
print(t.time())
# 设置了别名之后,原始的名称则不能再使用,不然会提示未定义。
# print(time.time()) # name 'time' is not defined
这种别名功能,一般在用于对某个第三方模块或自定义模块的模块名称较长的时候,使用别名功能。
4、模块是第一类对象
import time
x1ong = time # 模块名可以作为值赋值给某个变量
# 都可以调用time()函数
print(x1ong.time())
print(time.time())
5、Python3的时候,所有的自定义模块都应该以纯小写+下划线的命名风格规范,但是在python2的时候,有使用驼峰命名法的规范,但是到了Python3之后,都改用了纯小写+下划线的命名风格。

6、可以在函数内导入模块
def func():
import time
print(time.time())
func()
# time.time() # 报错
但是在函数内导入的模块,无法在函数外使用,这跟函数内的变量,无法在函数外使用一个道理。
如果不是功能的需要,建议在Python的文件开头导入模块。
模块和当前文件的名称空间
test.py文件的内容如下:
import x1ong
x = 1
y = 2
print('1111')
print('2222')
print(x1ong.x)
print(x1ong.get)
print(x1ong.change)
x1ong模块下的内容如下:
print('from x1ong moudel >>> ')
x = 1
def get():
print(x)
def change():
global x
x = 0
他们的关系图为:

可以发现,模块x1ong与test.py文件中定义的变量,不再一个名称空间中,但是我们通过x1ong.get调用模块x1ong的内容时,则是使用引用的方式,引用到get的内存地址。
__name__这个属性在每一个Python文件中都有值,我们可以通过print(__name__)打印。结果是一个字符串类型
如果一个文件是被作为模块引用进来的,那么该文件的__name__属性返回的值就为模块的名称,即文件名称。
如果一个文件没有被作为模块引用,那么该文件的__name__属性返回的值为'__main__'
我们常常使用如下if语句,判断当前文件是否被作为模块引用:
if __name__ == '__main__':
# 文件没有被引用执行的代码
print('文件没有被引用')
else:
# 文件被引用了执行的代码
print('文件是被引用的')
from..import..用法
在讲from..import..之前,我们先总结一下import功能的优点与缺点:
优点:绝对不会与当前名称空间的名称冲突
缺点:要加上模块的名称即模块名.做前缀,比较麻烦。
from..import..可以从一个模块中,单独导入一个名称,并且不用加模块名.的前缀。
from x1ong import x
from x1ong import get
print(x)
print(get)
from..import..导入发生的三件事
1、产生一个模块的名称空间
2、运行x1ong.py的时候,将运行过程中产生的名字都存放到模块的名称空间中。
3、在当前名称空间拿到一个名字,该名字与名称空间中某一个内存地址做绑定。
from..import..的优缺点
优点:在调用模块下某个名称的时候,不需要加上前缀。即模块名.
缺点:模块中的名称容易与当前名称空间的名称混淆。
from..import..的其他知识
1、在一行导入多个名字,中间使用逗号进行间隔
# from x1ong import x
# from x1ong import get
# from x1ong import change
from x1ong import x,get,change # 不太推荐使用
如果在一个模块中,使用的名称稍微比较多,我们可以使用这种方法。
# *号: 可以导入一个模块中的所有名字
from x1ong import * # 但是不推荐使用,容易混淆。因为*导入的为模块中所有的名字,会与我们当前文件的名称冲突。
# 了解:__all__
# 其实*查找的就是模块文件中的__all__属性的值,该值是一个列表,存放的为当前文件的所有名称。
__all__ = ['x','change'] # 为模块x1ong添加右侧内容
之后我们再test.py导入模块x1ong
from x1ong import *
get() # 报错,提示未定义,因为模块x1ong的__all__的列表中,没有名字get
print(x) # 正常 1
print(change) # 正常
循环导入问题
循环导入指的是在一个模块加载或导入中导入了另外一个模块,而在另外一个模块中,又返回来导入第一个模块中的某一个名字,由于第一个模块,未加载完毕,所以引出失败,异常等情况。原理就是在python中,同一个模块只会在第一次导入时执行其内部代码,再次导入该模块时,即便是该模块尚未完全加载完毕也不会去重复执行内部代码。
我们使用以下示例来演示循环导入的问题:
m1.py文件内容如下:
print('正在导入m1...')
from m2 import y
x = 10
m2.py文件内容如下:
print('正在导入m2...')
from m1 import x
y = 20
test.py文件内容如下:
import m1
运行test.py抛出的错误:
正在导入m1...
Traceback (most recent call last):
正在导入m2...
File "/Users/x1ong/Downloads/test.py", line 1, in <module>
import m1
File "/Users/x1ong/Downloads/m1.py", line 2, in <module>
from m2 import y
File "/Users/x1ong/Downloads/m2.py", line 2, in <module>
from m1 import x
ImportError: cannot import name 'x' from 'm1' (/Users/x1ong/Downloads/m1.py)
[Finished in 254ms with exit code 1]
# 分析:
'''
运行test.py之后————>import m1,开始导入m1并运行其他内部代码 ————> 打印内容 "正在导入m1..." 执行 from m2 import y
开始导入m2并运行其内部代码,之后打印"正在导入m2...",执行from m1 import x,由于m1已经被导入过了,不会再重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错。
'''
解决方案:
# 方案1:导入语句放到最后,保证所有名字都被加载
# 文件: m1.py
print('正在导入m1...')
x = 10
from m2 import y
# 文件: m2.py
print('正在导入m2...')
y = 20
from m1 import x
# 此时再运行test.py,即可正常运行。
# 解决方案2:导入语句放到函数中,只有在调用函数时才会执行其内部代码
# 文件: m1.py
print('正在导入m1...')
x = 10
def f1():
from m2 import y
print(x,y)
# 文件: m2.py
print('正在导入m2...')
y = 20
def f2():
from m1 import x
print(x,y)
总结:循环导入问题基本上都是程序设计失误导致的,上述解决方案也非常的不推荐使用,尽量在实际开发中,规避这种循环导入的问题。如果多个模块需要共享一些名称,可以将共享的名称放到一个模块中,之后再进行导入。
搜索模块的路径与优先级
无论是import或者from..import..在导入一个模块的时候,如果该模块被加载到内存,则直接引用,否则就会在指定的位置去查找模块。之后按照从左到右的顺序去查找sys.path规定的路径。sys.path的返回值是一个列表。
x1ong@bogon downloads % python3 test.py
['/Users/x1ong/Downloads', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']
列表的每一个值都可以被当做一个目录去看,Python解释器会在该列表中每个元素指定的路径去寻找模块。
sys.path的第一个值,为我们当前Python文件所在绝对路径。
sys.modules返回的值为一个字典类型,它里面定义了我们所有加载到内存的模块名称。
编写模块的规范
我们在编写py文件的时候,需要时刻注意,我们的Python文件是给自己用的,还是要当做模块,去给比人去用,如果其他人还要使用,那么代码的可读性和可维护性就显得格外的重要。
'''
The module is used to .......
'''
import os # 导入其他模块
y = 20 # 非必要不适用全局变量,尽量使用局部
class Foo: # 定义类,做好对类的注释
'''
Class Foo is used to...
'''
pass
def func: # 定义函数,做好对函数的注释
'''
function func is used to...
''
pass
if __name__ == '__main__': # 主程序
func() # 在当做脚本执行的时候,执行这里面的代码。
本文作者为blog,转载请注明。