python模块的含义
模块简介
模块的本质
模块是内部具有一定功能(代码)的py文件
python模块的历史
python刚开始的时候其他编程语言的程序员都看不起,甚至给python起了个外号:调包侠
随着时间的发展,发展项目的复杂度越来越高,python的模块功能越来越好用,上面那一帮人也不得不用一下python,然后发现python真香!
python模块的表现形式
- py文件(py文件也可以称之为模块文件)
- 含有多个py文件的文件夹(按照模块功能的相似度 分在同一个文件夹 这个文件夹叫包)
- 已被编译为共享库或者DLL的c或C++扩展(了解)
- 使用C编写并连接到python解释器的内置模块(了解)
模块的分类
- 自定义模块:自己写的模块文件,如py文件
- 内置模块:Python解释器提供的模块
- 第三方模块:别人写的模块文件。很多大佬写的模块很好用。
导入模块的两种句式
强调
- 一定要搞清楚谁是被执行文件 谁是导入文件
- py文件是纯英文 不会用中文或空格
- 导入模块文件不需要填写后缀名
import句式
# 以import a为例研究底层原理
1.先产生执行文件的名称空间
2.执行被导入文件的代码将产生的名字放入被导入文件的名称空间中
3.在执行文件的名称空间中产生一个模块的名字
4.在执行文件中使用该模块名点的方式使用模块名称空间中所有的名字
import流程推导
如图:
- 先运行左边
import a
代码所在的py文件。产生执行文件的全局名称空间,里面什么也没有。 - 看见
import a
,产生a.py文件的全局名称空间,开始运行a.py文件。 - 在a.py全局名称空间产生name这个名字
- 在a.py全局产生func1、func2、change函数名
- 模块运行完毕 标志
import a
运行完毕 被执行文件名称空间多一个名字a,对应着a模块。
- a.py运行完毕回来执行
print(a.name)
,此时调用的name是a.py里面的name,结果是'jason'。 - 再继续往下
a.func1()
,调用的是a.py模块的func1。
练习
print(name)
的 结果是什么?
a.change()
修改的是a.py文件名称空间内的全局name,修改不到执行文件的。
所以print(name) # kevin
from...import...句式
# 以from a import name,func1为例研究底层原理
"""
1.先产生执行文件的名称空间
2.执行被导入文件的代码将产生的名字放入被导入文件的名称空间中
3.在执行文件的名称空间中产生对应的名字绑定模块名称空间中对应的名字
4.在执行文件中直接使用名字就可以访问名称空间中对应的名字
"""
- 这里
print(name)
输出的就是a.py文件中的'jason'
- 如果在执行文件中改了name的值,那也就拿不到模块文件中的'jason'了。pycharm还会对name标黄。
补充说明
-
import与from...import...两者优缺点
import句式
由于使用模块名称空间中的名字都需要模块名点的方式才可以用
所以不会轻易的将被执行文件中的名字替换掉
但是每次使用模块名称空间中的名字都必须使用模块名点才可以from...import...句式
指名道姓的导入模块名称空间中需要使用的名字 不需要模块名点
但是容易跟执行文件中名字冲突 -
重复导入模块
解释器只会导入一次 后续重复的导入语句并不会执行
结果:
-
起别名
import ..... as ...
from ..... import ..... as ...
from a import name as n,func1 as f1
例子:
结果:
例子:
结果:
例子:
结果:
- 涉及到多个模块导入
import say
import tell
如果模块功能相似度不高 推荐使用第一种 相似度高可以使用第二种
import say, tell
循环导入
1.循环导入
两个文件之间彼此导入彼此并且相互使用各自名称空间中的名字 极容易报错
2.如何解决循环导入问题
1.确保名字在使用之前就已经准备完毕
2.我们以后在编写代码的过程中应该尽可能避免出现循环导入
流程推导
- 运行md1.py文件,产生md1全局名称空间,
import md2
去执行md2模块 - 运行md2.py文件,产生md2全局名称空间,注意这里
import md1
此时md1名称空间已经展开,此时会回到md1文件继续运行 - 运行
name = 'from md1'
代码,在md1全局产生name名字。 - 运行
print(md2.name)
代码,此时会去md2找name这个名字,显然没有,所以报错!!
报错信息还会提示你,可能是由于循环导入(circular import):
解决
- 确保名字在使用之前就已经准备完毕 把这个12345这个流程顺序搞明白!
- 在函数体代码里写import代码
判断文件类型
所有的py文件都可以直接打印__name__对应的值
当py文件是执行文件的时候__name__对应的值是__main__
当py文件是被导入文件的时候__name__对应的值是模块名
if __name__ == '__main__':
print('哈哈哈 我是执行文件 我可以运行这里的子代码')
上述脚本可以用来区分所在py文件内python代码的执行
使用场景
1.模块开发阶段
2.项目启动文件
"""
from a import * *默认是将模块名称空间中所有的名字导入
__all__ = ['名字1', '名字2'] 针对*可以限制拿的名字
"""
运行md.py文件:
结果:
限制import * 导入的方法
星号可以一次性导入所有名字 这样很容易产生名字冲突
可以用一个东西 限制星号的导入 也就是在模块文件里加代码__all__
会报错:这也表示限制成功了!!
双下all只针对星号导入,如下导入方法,双下all无法限制!!!
模块的查找顺序(重要)
1.内存
import aaa
import time
time.sleep(15)
print(aaa.name)
aaa.func1()
# 在内存开辟名称空间!!删除文件只是删除硬盘中的数据 程序结束之后 内存中跟程序相关的数据删掉了 内存释放了 再次运行就找不到了
2.内置
import time
print(time)
print(time.name)
"""
以后在自定义模块的时候尽量不要与内置模块名冲突
"""
# 1. 有自定义模块 和 内置模块时 优先找内置
# 2. 在内置模块 和 自定义模块重名时 会把pycharm搞懵 你调用模块时pycharm 会把自定义和内置的名字都列出来
3.执行文件所在的sys.path(系统环境环境)
一定要以执行文件为准!!!
我们可以将模块所在的路径也添加到执行文件的sys.path中即可
import sys
print(sys.path) # 列表
sys.path.append(r'D:\pythonProject03\day17\mymd')
import ccc
print(ccc.name)
将路径导入到sys.path列表 把pycharm都玩懵了 还飘红 但是可以执行
绝对导入和相对导入
"""
再次强调:一定要分清楚谁是执行文件!!!
模块的导入全部以执行文件为准
"""
绝对导入
from mymd.aaa.bbb.ccc.ddd import name # 可以精确到变量名
from mymd.aaa.bbb.ccc import ddd # 也可以精确到模块名
ps:套路就是按照项目根目录一层层往下查找
相对导入
.在路径中表示当前目录
..在路径中表示上一层目录
..\..在路径中表示上上一层目录
不在依据执行文件所在的sys.path 而是以模块自身路径为准
from . import b
相对导入只能用于模块文件中 不能在执行文件中使用 一旦使用相对导入 就表示这个文件是模块文件 不是执行文件。
'''
相对导入使用频率较低 一般用绝对导入即可 结构更加清晰
'''
流程推导
# 1.你要在a.py文件用from...import导入
# 2.出发点必须是整个项目路径 一层层用点 点出来
# 3.如下图python_goldclass就是我项目路径下的一个包 helloworld是个py文件
执行文件是谁问题(重要)
上图在第5步这里出问题,因为此时的执行文件是md.py,此时我们只看md文件的路径,md.py文件路径下没有一个叫b的模块!!! 所以报错 No module named 'b'
修改成如下则没问题:
如果路径很深这样导入会很麻烦,要写很长:
所以此时就可以使用相对导入,用点表示在当前模块文件所在路径找!!
相对导入只能用于模块文件中 不能在执行文件中使用!!! 会直接将这个文件定死为模块文件?
包
大白话:多个py文件的集合>>>:文件夹
内部含有双下init.py文件的文件夹 (python2必须要求 python3无所谓了)
python3你是一个有py文件的文件夹 你就是包
1.如果只想用包中某几个模块 那么还是按照之前的导入方式即可
from aaa import md1, md2
2.如果直接导入包名
import aaa
导入包名其实就是导包下面的__init__.py文件,该文件内有什么名字就可以通过包名点什么名字