模块就是一系列功能的集合体,分为三大类:
模块有四种形式:
文件名:foo.py
文件内容:
print('模块foo前来报到')
x = 1
def get():
print(x)
def change():
global x
x = 0
我们现在定义了一个模块,模块名为foo。
要注意:自定义模块的命名应该采用纯小写+下划线的格式
要想在另外一个py文件中引用foo模块中的功能,需要使用import foo导入。
import foo # 模块foo前来报到
我们可以看到模块成功导入,并执行了模块代码。
当我们需要导入多个模块的时候,可以用逗号为分隔符在一行导入多个模块:
import time, foo, m
# 但不建议这么做,因为我们有导入模块的规范。
我们导入的模块中可能包含有python内置的模块、第三方的模块、自定义的模块。为了便于明显地区分它们,我们通常在文件的开头导入模块,并且分类导入。一类模块的导入与另外一类的导入用空行隔开,不同类别的导入顺序如下:
import time
import sys
import 第三方1
import 第三方2
import 自定义模块1
import 自定义模块2
import 自定义模块3
执行模块的源文件代码
产生一个新的名称空间用于存放模块的源文件执行过程中产生的名字
在当前执行文件所在的名称空间中得到一个名字foo,该名字指向新创建的模块名称空间,若要引用模块名称空间中的名字,需要加上该前缀,如下:
import foo
a = foo.x # 引用模块foo中变量x的值赋值给当前名称空间中的名字a
foo.get() # 调用模块foo的get函数
foo.change() # 调用模块foo中的change函数
print(foo.x)
# 运行结果:
# 模块foo前来报到
# 1
# 0
在首次导入模块之后重复导入相同模块的行为,都是直接引用首次导入产生的模块名称空间,不会重复执行代码。
“ 模块名 . 名字 ”,是指名道姓地问某一个模块要名字对应的值,不会与当前名称空间中的名字发生冲突。
import foo
x = 1111111111111
print(x)
print(foo.x)
# 运行结果:
# 模块foo前来报到
# 1111111111111
# 1
无论是查看还是修改,操作的都是模块本身,与调用位置无关。(名称空间的相互关系是在定义的时候决定的)
import foo
x = 1111111111111
foo.get() # 1
foo.change() # foo.py里的x被修改
foo.get() # 0
只用 import导入模块的方法在使用时必须加前缀 "模块 ."
优点:肯定不会与当前名称空间中的名字冲突
缺点:加前缀显得麻烦
通常在被导入的名字过长时采用起别名的方式来精简代码,另外为被导入的名字起别名可以很好地避免与当前名字发生冲突。
import foo as f
f.get() # 1
def func():
import foo
from…import…与import语句基本一致,唯一不同的是,使用import foo导入模块后,引用模块中的名字都需要加上foo.作为前缀;而使用from foo import x, get, change 的形式,则可以在当前执行文件中直接引用模块foo中的名字,如下:
from foo import x,get,change # 将模块foo中的x、get、change导入到当前名称空间
a=x # 直接将模块foo中的x指向的内存地址赋值给当前文件全局名称空间的a(让a指向x指向的内存地址)
get() # 直接执行foo中的get函数
change() # 即便是当前文件中有重名的x,修改的仍然是源文件中的x
如果当前名称空间存在相同的名字,则后定义的名字会覆盖之前定义的名字,如下:
x = '惊喜'
from foo import x
print(x) # 1
产生一个模块的名称空间
运行foo.py,将运行过程中产生的名字都丢到模块的名称空间去
在当前文件的名称空间中产生一个名字,该名字与模块名称空间中相同的名字指向同一个内存地址
from foo import x # x = 模块foo中值1的内存地址
from foo import get
from foo import change
print(x) # 1
print(get) #
print(change) #
x = 333333333
print(x) # 333333333
change()
get() # 0
print(x) # 333333333
from foo import x # x = 模块foo中值1的内存地址
from foo import get
from foo import change
print(x) # 1
change() # 只改变的是模块foo中x的值,当前文件名称空间中的x在导入模块的时候绑定的值是1,模块内x绑定的值再更改不会影响当前文件名称空间中的x。
get() # 0
print(x) # 1
使用 from…impot…导入模块在使用时不用加前缀
优点:代码更精简
缺点:容易与当前名称空间混淆
from foo import *
print(x)
print(get)
print(change)
# 运行结果:
# 模块foo前来报到
# 1
#
#
如果我们需要引用模块中的名字过多的话,可以采用上述的导入形式来达到节省代码量的效果,但是需要强调的一点是:只能在模块最顶层使用 * 的方式导入,在函数内则非法。并且 * 的方式会带来一种副作用,即我们无法搞清楚究竟从源文件中导入了哪些名字到当前文件,这极有可能与当前位置的名字产生冲突。
所以模块的编写者可以在自己的文件中定义_ _all_ _变量用来控制 * 代表的名字范围:
# 在 foo.py 中添加:
_ _all_ _ = ['x'] # 控制 * 代表的名字有哪些
在当前文件导入:
from foo import * # 此时的 * 只代表 x
print(x) # 1
print(get) # 报错:NameError: name 'get' is not defined
print(change) # 报错:NameError: name 'change' is not defined
from foo import get as g
g()
# 运行结果:
# 模块foo前来报到
# 1
if __name__ == '__main__':
print('python文件运行') # 直接运行foo.py文件输出这句话
else:
print('文件被当做模块导入') # 被当做模块通过import导入输出这句话
在导入一个模块时,如果该模块已加载到内存中,则直接引用,否则会优先查找内置模块,然后按照从左到右的顺序依次检索sys.path中定义的路径,直到找到模块对应的文件为止,否则抛出异常。
import sys
print(sys.path)
# 值为一个列表,存放了一系列的对文件夹
# 其中第一个文件夹是当前执行文件所在的文件夹
import sys
# 找foo.py就把foo.py的文件夹的绝对路径添加到环境变量中
sys.path.append(r'/Users/用户/PycharmProjects/Demo/test/8.20')
注意:这种添加是临时添加,程序运行结束后会恢复原样。
import sys
# print('foo' in sys.modules) # False
print(sys.modules)
我们在编写模块的py文件时,需要时刻提醒自己,该文件既是给自己用的,也有可能会被其他人使用。所以代码的可读性与易维护性就显得十分重要,为此我们在编写一个模块时最好按照统一的规范去编写,如下:
"The module is used to..." #模块的文档描述
import sys # 导入模块
x=1 # 定义全局变量,如果非必须,则最好使用局部变量,这样可以提高代码的易维护性,并且可以节省内存提高性能
class Foo: # 定义类,并写好类的注释
'Class Foo is used to...'
pass
def test(): # 定义函数,并写好函数的注释
'Function test is used to…'
pass
if __name__ == '__main__': # 主程序
test() # 在被当做脚本执行时,执行此处的代码