自定义模块
模块的定义与分类
定义 : 模块,就是一些列常用功能的集合体。
模块的作用(优点):
- 程序的结构更清晰,方便管理,实现了功能的重复利用
- 拿来主义,提升开发效率
- 避免重复造轮子
script (脚本)
- 将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script,脚本就是一个python文件
模块的分类
第一类 : 内置模块 (标准库)
- 由python解释器提供,例 time 模块 os 模块,标准库模块约(200多个,每个模块又有很多功能)
第二类 : 第三方模块 (第三方库)
- python大神写的非常好用的模块,必须通过 pip install 指令安装的模块,比如BeautfulSoup, Django,等等。大概有6000多个
第三类 : 自定义模块
- 自己在项目中定义的一些模块
import 直接引用
import 使用
print('from the tbjx.py') # 整体是 tbjx 模块
name = '太白金星'
def read1():
print('tbjx模块:',name)
def read2():
print('tbjx模块')
read1()
def change():
global name
name = 'barry'
模块包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块import很多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 import tbjx #只在第一次导入时才执行tbjx.py内代码,此处的显式效果是只打印一次'from the tbjx.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
第一次导入模块执行三件事
- 将模块文件加载到内存.(在执行文件中执行一次此模块)
- 在内存中创建 一个 以 导入模块 命名的名称空间.
- 通过 导入模块 名称空间的 模块 名字 + . 等方式引用此模块的名字(变量,函数名,类名等等).
- ps:重复导入会直接引用内存中已经加载好的结果
被导入模块有独立的名称空间
- 每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
- 坑 : 通过 导入模块 名称空间的 模块 名字 + . 的方式引用 此模块的名字时,一定一定是从 此模块 中寻找.
- 通过 import 引用模块 他有自己的 独立名称空间 , 与当前 执行文件 没有关系.
为模块起别名
- 别名其实就是一个外号
import tbjx as t
t.read1()
别名 应用 :
- 好处可以将很长的模块名改成很短,方便使用
- 有利于代码的扩展和优化 , 便于统一维护
导入多个模块
开发过程中,免不了会在一个文件中,导入多个模块,推荐写法是一个一个导入 , 即分开多行导入
多行导入优点:
- 易于阅读 / 易于编辑 / 易于搜索 / 易于维护
import os,sys,json # 这样写可以但是不推荐
# 推荐写法
import os
import sys
import json
导入模块顺序
内置模块 → 第三方模块 → 自定义模块
- 模块的导入遵循PEP8规范:所有的模块导入都应该尽量放在这个文件的 开头
from ... import ...
from ... import ... 使用
# from ..(模块名). import . (模块里的内容).. 的使用示例。
from tbjx import name, read1 #相当于从tbjx模块的全局空间中将name,read1变量与值的对应关系复制到当前执行文件的全局名称空间中.
print(name)
read1()
'''
执行结果:
from the tbjx.py
太白金星
tbjx模块: 太白金星
'''
from...import... 与import对比
唯一的区别就是:
- 使用from...import...则是将 导入模块 中的 变量名字 直接导入到 当前的 名称空间 中,所以在当前名称空间中,直接使用名字就可以了、无需加前缀:模块名 .
- 好处:使用起来方便了
- 坏处:容易与当前执行文件中的名字冲突 / 如果当前空间有变量名与导入模块的变量相同 , 后者将把前者覆盖 即↓
- 执行文件有与模块同名的变量或者函数名,会有 覆盖效果 由下往上覆盖 ↑
当前位置直接使用read1和read2就好了,执行时,仍然以tbjx.py文件全局名称空间
一行导入多个 / 起别名
from tbjx import read1,read2,name
from ... import * (别单独用) 与 __all__
结合使用
__all__
- 将模块中所有的变量名字复制过来,
- 容易覆盖
__all__
结合使用__all__
写在 模块文件 里
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
py文件的两种功能
编写好的一个python文件可以有两种用途:
脚本( 承载文件 ) / 执行文件
__name__
(在模块中调试数据)
- 一个文件就是整个程序,用来被执行
- 直接打印
__name__
返回值为 '__main__
'
模块 / 被执行文件
- 文件中存放着一堆功能,用来被导入使用
- 直接打印
__name__
返回值为 模块名
python为我们内置了全局变量 __name__
:
- 当文件当做脚本执行时:
__name__
等于'__main__
' - 当文件当做模块被执行时:
__name__
等于模块名
作用:
- 用来控制.py文件在不同的应用场景下执行不同的逻辑(或者是在模块文件中测试代码)
模块循环导入问题
#创建一个m1.py
print('正在导入m1')
from m2 import y
x='m1'
#创建一个m2.py
print('正在导入m2')
from m1 import x
y='m2'
#创建一个run.py
import m1
#测试一
执行run.py会抛出异常
正在导入m1
正在导入m2
Traceback (most recent call last):
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/aa.py", line 1, in <module>
import m1
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
from m1 import x
ImportError: cannot import name 'x'
#测试一结果分析
先执行run.py--->执行import m1,开始导入m1并运行其内部代码--->打印内容"正在导入m1"
--->执行from m2 import y 开始导入m2并运行其内部代码--->打印内容“正在导入m2”--->执行from m1 import x,由于m1已经被导入过了,所以不会重新导入,所以直接去m1中拿x,然而x此时并没有存在于m1中,所以报错
#测试二:执行文件不等于导入文件,比如执行m1.py不等于导入了m1
直接执行m1.py抛出异常
正在导入m1
正在导入m2
正在导入m1
Traceback (most recent call last):
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m2.py", line 2, in <module>
from m1 import x
File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa练习目录/m1.py", line 2, in <module>
from m2 import y
ImportError: cannot import name 'y'
#测试二分析
执行m1.py,打印“正在导入m1”,执行from m2 import y ,导入m2进而执行m2.py内部代码--->打印"正在导入m2",执行from m1 import x,此时m1是第一次被导入,执行m1.py并不等于导入了m1,于是开始导入m1并执行其内部代码--->打印"正在导入m1",执行from m1 import y,由于m1已经被导入过了,所以无需继续导入而直接问m2要y,然而y此时并没有存在于m2中所以报错
# 解决方法:
方法一:导入语句放到最后
#m1.py
print('正在导入m1')
x='m1'
from m2 import y
#m2.py
print('正在导入m2')
y='m2'
from m1 import x
方法二:导入语句放到函数中
#m1.py
print('正在导入m1')
def f1():
from m2 import y
print(x,y)
x = 'm1'
# f1()
#m2.py
print('正在导入m2')
def f2():
from m1 import x
print(x,y)
y = 'm2'
#run.py
import m1
m1.f1()
模块的搜索路径
内存中已经加载的模块 > 内置模块 > sys.path路径中包含的模块
import sys
# print(sys.path)
sys.path.append(r'D:s15')
# import tbjx
import tbjx1
tbjx1.read1()
需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。虽然每次都说,但是仍然会有人不停的犯错