模块基础
Python文件的两种运行方式
1、脚本方式:
- 直接解释器执行。
2、模块方式:
- 通过导入语句被其他文件导入,为导入它的文件提供资源(变量,函数定义,类定义等)。
模块的定义
- 本质就是文件,一系列功能的集合体。
- 模块中出现的变量,for循环,if结构,函数定义。。。等称为模块的成员。
对一些通用的功能,将它们提出来放进一个模块中,然后在各个地方导入使用,这样能增加开发效率,减少代码冗余。
模块的四种通用的类别
1、一个Python文件就是一个模块,文件名test.py,模块名则为test。
2、盛放有多个Python文件的目录也是一个功能的集合体,也是一种模块,称之为包。
3、已被编译为共享库或DLL或C++扩展。
4、使用C编写并链接到Python解释器的内置模块。
模块的三种来源
1、自带的模块。
-
内置模块,集成在解释器内部。
import time print(time) <module 'time' (built-in)>
-
标准库
import os print(os) <module 'os' from 'D:\\Python38\\lib\\os.py'>
2、第三方模块
一些开发者写的非常好的模块,需要同过pip命令安装的模块,比如Django。
3、自定义模块。
我们自己写的一些模块。
模块导入
模块导入的两种语句:
import 模块名
from 模块名 import 成员
第一种的优点是能指名道姓的使用该模块的成员,不会产生名称冲突;缺点是每次使用都要加模块名前缀。
第二种的优点是使用简单,不用加模块名前缀;缺点是容易与当前命名空间的名称产生冲突。
模块导入的多种方式
导入一个模块的所有成员。
import module
一次性导入多个模块的所有成员,不推荐这种写法。
import module1, module2,module3......
从某个模块中导入指定的成员。
from module import a
从某个模块中导入多个成员。
from module import a,b,c
从模块中导入所有成员,和import的区别是这种方式不用加模块名前缀。
from module import *
使用from module import * 控制成员被导入
- 默认情况下,所有成员都会被导入。
- __all__是一个列表,用于表示本模块可以被外界使用的成员,元素是成员名组成的字符串。
# 在模块内使用'__all__'控制被导入的成员。
__all__ = [member1,member2,member3,...]
在文件中使用from module import *导入时,只能使用列表内的成员,若要使用模块的所有成员,可以不使用from module import * ,从而绕过__all__的限制。
怎么解决命名冲突的问题
- 使用import module 导入。
- 自己避免使用同名的名称。
- 使用别名解决冲突。
模块别名
主要针对导入的模块和导入模块的成员,并非针对我们自己写的变量、函数、类等
1、给成员起别名,避免命名冲突。
form module import member as mem_alias
2、给模块起别别名,简化书写。
import module as mod_alias
as是alias的缩写。
模块使用
使用模块名加点.
方式来调用模块下的成员。
import time
print(time.time()) # 表示使用time模块中的time()方法
首次导入模块发生的事
- 1、在内存中产生模块的名称空间,将模块运行的过程中产生的名字都丢到模块的名称空间中。
- 2、执行模块名称空间中所有可执行代码。
- 3、在当前文件中产生一个模块名称,该名称指向模块中产生的名称空间 。
导入语句可以在程序中任意位置使用,为防止针对同一模块的多次导入重复占用内存空间,Python的优化手段是:
第一次导入后就将模块名加载到内存了,后续的import语句进是针对已经加载到内存的模块对象增加了一次引用,不会重复执行模块内的语句。
导入后的名称空间
- 被导入的模块有独立的名称空间,无论是查看还是修改操作的都是原模块,与调用位置无关
- 模块的名称空间没有被引用后,则会被回收。
- 模块也可以在函数中导入,导入后则只能在导入时的名称空间内使用,函数执行完毕后则名称空间被回收。
def func():
import time
print(time.time())
# 函数外则无法调用
time.time()
模块的导入规范
约定俗称的顺序,便于读代码
- 1、内置模块。
- 2、第三方模块。
- 3、自定义模块。
循环导入问题
指的是模块之间互相导入。例如,
# 模块1中导入模块2。
from m2 import n
i = 1
# 模块2中导入模块1。
from m1 import i
n = 2
如果这时候,我们要在另一个执行文件中导入其中一个模块,就会报错。
import m1
ImportError: cannot import name 'i' from partially initialized module 'm1' (most likely due to a circular import)
这是因为,在当前文件导入m1时,m1的代码执行导入m2,m2的代码执行又导入m1中的变量i
,但此时变量i
在m1中还没有定义(因为第一行from还没有执行完毕),所以会报错无法导入i
。
首先,循环导入在构建代码逻辑时是一定要避免的,但若你的代码逻辑走到这一步,那么解决方式有4种:
1、在导入模块前定义名称。
n = 2
from m1 import i
2、将导入模块语句写在函数中,之后的函数调用在定义名称之后。
def func():
from m1 import i
n = 2
func()
3、使用一个新文件,将俩模块公用部分放入新文件中。
4、使用import导入也能避免该问题。
控制模块被导入时是否运行
一个模块往往包含多个功能,自定义模块被其他模块导入时,其中的可执行语句会立即执行,一般我们不希望模块内的函数直接执行,仅在需调用其中某些功能时才执行,解决方式是使用全局变量 _name_。
- 当文件被当做脚本执行时,在文件内执行 print( _name_) 返回值是固定字符串 '__main__',表示当前是开发模式。
print(__name__)
__main__
- 当文件被当做模块导入时,在模块内的 _name_ 等于模块原名(不包含.py后缀),表示当前是导入模式。
# 在模块中判断__name__是否为'__main__'
import time
print(time.__name__)
time
使用__name__这个特性帮我们区分模块的两种运行方式:
def func():
pass
if __name__ == '__main__':
func()
# 如果为True则表示是开发模式,则执行函数。
# 如果为False则是导入模式,则不会执行
此语句经常使用,Pycharm提供了一个快捷方式,直接输入 main 然后回车即可。
模块的搜索路径优先级
Python中导入模块是按照一定的规则以及顺序去寻找的。
1、内存中
- 如果之前成功导入过某个模块,则直接使用已经存在的模块。
# sys.modules查看已经加载到内存中的模块
import sys
print(sys.modules)
输出为一个字典,模块名为key,value是导入位置
2、内置模块
- Python解释器的安装路径中Lib目录下,所有以 .py结尾的文件,都是自带模块。
- Lib目录中的site-packages目录,一些来源网络的第三方模块,就放在这个目录中。
3、sys.path路径
- 是一个路径的列表。通常导入自定义的模块时,将自定义模块的目录添加至此列表中。
import sys
print(sys.path)
# ['D:\\Desktop\\Netfile\\Python\\Program\\Py_programs',
'D:\\Desktop\\Netfile\\Python\\Program\\Py_programs', 'D:\\PyCharm
2020.1.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\Python38\\python38.zip',
'D:\\Python38\\DLLs', 'D:\\Python38\\lib', 'D:\\Python38',
'D:\\Python38\\lib\\site-packages', 'D:\\PyCharm
2020.1.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
第一个路径是当前执行文件的所在目录,第二个目录在pycharm中的话,它会做一个优化,将当前项目这个顶级目录也添加入sys.path列表中。
添加模块所在目录的绝对路径
对于我们自定义的模块或下载的第三方模块,如果模块的所在目录不在sys.path列表中,那么就不能直接导入,解决方式就是在列表中添加模块所在目录的路径。
sys.path.append(r'/path/moudir')
sys.path添加路径是临时添加,程序运行结束后恢复。
sys.path环境变量是以执行文件为准的,被导入的模块或后续其他文件引用的sys.path都是参照执行文件的sys.path。
import sys
sys.path.append('abcdefghijklmnopqrstuvwxyz')
print(sys.path)
import modu1 # modu1中有一行print(sys.path)语句
print('下面是模块的sys.path.............')
结果为:
['D:\\Desktop\\Netfile\\Python\\Program\\Py_programs', 'D:\\Desktop\\Netfile\\Python\\Program\\Py_programs', 'D:\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\Python38\\python38.zip', 'D:\\Python38\\DLLs', 'D:\\Python38\\lib', 'D:\\Python38', 'D:\\Python38\\lib\\site-packages', 'D:\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend', 'abcdefghijklmnopqrstuvwxyz']
下面是模块的sys.path.............
['D:\\Desktop\\Netfile\\Python\\Program\\Py_programs', 'D:\\Desktop\\Netfile\\Python\\Program\\Py_programs', 'D:\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\Python38\\python38.zip', 'D:\\Python38\\DLLs', 'D:\\Python38\\lib', 'D:\\Python38', 'D:\\Python38\\lib\\site-packages', 'D:\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend', 'abcdefghijklmnopqrstuvwxyz']