模块的导入使用

1、什么是模块数据库

模块就是一系列功能的集合体,一个模块就是一个包含了Python定义和声明的文件,文件名就是模块名字加上.py的后缀。app

模块有三种来源:spa

  一、内置的模块3d

  二、第三方的模块日志

  三、自定义模块code

模块的四种通用类别:blog

  一、使用Python编写的代码(.py文件)内存

  二、已被编译为共享库或DLL的C或C++扩展开发

  三、把一系列模块组织到一块儿的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)字符串

  四、使用C编写并连接到Python解释器的内置模块

2、为什么要用模块

  一、使用内置的或者第三方模块的好处是:拿来就可使用,能够极大提高开发效率

  二、使用自定义模块的好处是:能够减小代码冗余(抽取咱们本身程序中要公用的一些功能定义成模块,而后程序的各部分组件都去模块中调用共享的功能)

3、模块的使用

前提:必定要区分开谁是执行文件,谁是被导入模块

一、import导入模块

在Pycharm中右键新建一个文件,文件名是spam.py,模块名是spam,再新建一个文件叫run.py,在run.py中

import spam

首次导入一个模块,会发生如下事情:

  1)会产生一个模块的名称空间

  2)执行文件spam.py,将执行过程当中产生的名字都放到模块的名称空间中

  3)在当前执行文件的名称空间中拿到一个模块名,该名字指向模块的名称空间

以后的导入,都是直接引用第一次导入的成果,不会从新执行文件

在spam.py文件中添加代码

money = 1000

def read1(): print('spam模块:',money) def read2(): print('spam模块') read1() def change(): global money money = 0

在执行文件中访问模块名称空间中名字的语法:模块名.名字,即在run.py中

import spam print(spam.money)   # 1000
print(spam.read1)   # <function read1 at 0x000001CE16209B70>
print(spam.read2)   # <function read2 at 0x000001CE16209A60>
print(spam.change)  # <function change at 0x000001CE16209C80>
spam.read1()    # spam模块: 1000

def read1(): print('run.py --> read1') read1() # run.py --> read1
 spam.read2() # spam模块
                # spam模块: 1000
money = 9999 spam.change() print(spam.money)   # 0
print(money)        # 9999

总结 import 导入模块:在使用时必须加上模块名做为前缀

优势:指名道姓的向某一个名称空间拿取名字,不会与当前名称空间中的名字冲突

缺点:但凡应用模块中的名字都须要加前缀,不够简洁

还能够一行导入多个模块,但不推荐使用

import spam, os, time

能够为模块起别名(注意:模块名应全为小写)

import spam as sm print(sm.money) print(sm.read1)

二、from...import...导入模块

首次导入模块发生的三件事:

  1)建立一个模块的名称空间

  2)执行文件spam.py,将执行过程当中产生的名字都放到模块的名称空间中

  3)在当前执行文件中直接拿到一个名字,该名字就是执行模块中相对应的名字

删除上面的两个文件,从新建一个spam.py,添加代码

money = 1000

def read1(): print('spam模块:',money) def read2(): print('spam模块') read1() def change(): global money money = 0

再新建一个run.py,在run.py中

from spam import money print(money)  # 1000
from spam import money money = 200  
print(money)  # 与当前名称空间中的名字冲突, 结果为200

总结from...import...导入模块

优势:使用时,无需再加前缀,更简洁

缺点::容易与当前名称空间中的名字冲突

如今将run.py里的代码改为

from spam import money, read1 money = 200

# 这里执行read1里面的money仍是1000,由于在定义阶段就已经固定死了,与调用位置无关
read1()         # spam模块: 1000

print(money)    # 200

还能够导入所有模块,但不推荐使用

# 星号表明从被导入模块中拿到全部名字
from spam import *

有一个 __all__ 的功能,将指定的模块名以字符串的形式存放,若是再使用星号导入模块,这时导入的就是 __all__里面的内容,而不是导入所有的模块,例如:

在spam.py中定义 __all__

__all__ = ['money', 'read1'] money = 1000

def read1(): print('spam模块:',money) def read2(): print('spam模块') read1() def change(): global money money = 0

在run.py中用星号导入

from spam import * read1() # spam模块: 1000
read2()     # 报错, 由于 __all__没有包含read2模块

能够为模块起别名

from spam import read1 as r1 r1()

 4、Python文件的两种执行方式

一、直接运行

二、做为模块导入

# 在m1.py中
def f1(): print('f1') print(__name__)    # 执行后是 __main__


# 在run.py中
import m1    # 执行后是 m1

即:当作脚本运行,__name__ 等于'__main__',当作模块导入,__name__等于模块名

因此这个条件是:if __name__ == '__main__': 用来控制.py文件在不一样的应用场景下执行不一样的逻辑

# 在m1.py中
def f1(): print('f1') if __name__ == '__main__': f1() # 在run.py中
import m1

5、模块的搜索路径

一、模块搜索路径的优先级

  1)内存中已经加载过的

  2)内置模块

  3)sys.path(第一个值是当前执行文件所在的文件夹)

模块搜索路径优先从内存中已加载过的开始查找,例如我新建一个m1.py和一个run.py,在run.py中导入模块m1

# m1.py
def f1(): print('from f1') # run.py
import time import m1 time.sleep(10)    # 程序睡眠10秒的过程当中删除m1.py
import m1 m1.f1() # 这里还能够执行

 删除m1.py文件后再导入执行不会报错,由于在删除的过程当中程序没有结束,再次导入是优先从内存中查找,找到了直接运行,但若是再次运行就会报错,由于内存已经回收,找不到m1模块了

注意:不该该将本身的模块名命名成与内置模块或第三方模块的名字相同

二、添加sys.path

新建m1.py和run.py,将m1.py放在dir1文件夹下,让dir1文件夹和run.py在同一级目录

# m1.py
def f1(): print('from f1')
f1()
# run.py import m1 # 这时候是找不到的,可是若是添加sys.path,将run.py改为以下

# run.py import sys sys.path.append('E://Test//dir1') # 将m1所在的文件夹路径添加到sys.path import m1

三、from...import...

仍是上面的状况,能够在run.py里面经过from...import...导入

 

from dir1 import m1

这是在sys.path中找到的,可是我没有添加sys.path,为何也能找到呢?

由于sys.path的第一个值是当前执行文件所在的文件夹,也就是Test文件夹(个人run.py和dir1的上一层是Test文件夹),因此说是以当前执行文件的sys.path为准,能够找到dir1,进而找到m1

基于上面的目录,我在dir1下新建一个文件夹叫dir2,在dir2中新建一个文件叫m2.py,如今的路径关系是:Test下面有一个run.py和dir1,dir1下有一个m1.py和dir2,dir2下有一个m2.py

m2中添加代码

def f2(): print('from f2')
f2()

能够经过from...import...导入

from dir1.dir2 import m2

 第一种特殊状况:模块的绝对导入

删除上面的全部文件,新建run.py和dir1文件夹,在dir1下新建m1.py和m2.py

# m1.py
def f1(): print('from f1') # m2.py
def f2(): print('from f2')

run想访问m1,由于不在同一级目录,因此要经过上面讲到的两种方法(添加sys.path或from...import导入)来访问(我用后者)

# run.py
from dir1 import m1

如今我执行run.py,能够经过run访问到m1

如今m1想访问m2,只须要在m1中导入m2便可

# m1.py
import m2 def f1(): print('from f1') m2.f2()

可是如今,我再执行run.py,可否经过run访问到m1再访问到m2呢?

不行!由于sys.path是以当前执行文件为准的,那么sys.path的第一个值就是 E://Test//,在m1里面导入m2的时候,去sys.path中没法找到m2,因此会报错

强调:全部被导入的模块参照的环境变量sys.path都是以执行文件为准的

那么怎么解决呢

# m1.py
from dir1 import m2 def f1(): print('from f1') m2.f2() # m2.py
def f2(): print('from f2') # run.py
from dir1 import m1 m1.f1()

注意:不要理解成sys.path是以执行文件所在的文件夹为准,由于sys.path有不少个值,而执行文件所在的文件夹仅仅只是sys.path中的一个值,这个执行文件所在的文件夹找不到后面还有不少值能够找

上面这种状况是以执行文件的sys.path做为参考点开始导入,称之为模块的绝对导入

优势:在执行文件与被导入的模块中均可以使用

缺点:全部导入都是以sys.path为参考点,导入麻烦

第二种特殊状况:模块的相对导入

参照当前所在文件的文件夹为起始开始查找,称之为模块的相对导入

符号:.(一个点)表明当前所在文件的文件夹,..(两个点)表明上一级文件夹,...(三个点)表明上一级的上一级文件夹

优势:导入更加简单

缺点:只能在被导入的模块中使用,不能在执行文件中用

如今在Test下新建一个dir0,dir0下新建一个dir1和run.py,dir1下新建m1.py和m2.py

# run.py
from dir0.dir1 import m1    # 用的仍是绝对导入
 m1.f1() # m1.py
from . import m2    # 用的是相对导入

def f1(): print('from f1') m2.f2() # m2.py
def f2(): print('from f2')

 6、软件开发的目录规范

以 “ATM+购物车” 举例

ATM + 购物车 bin: 整个程序的执行文件,入口(调用核心逻辑的一些功能) - start.py conf: 配置文件(程序组件共用的变量) - settings.py lib: 库(自定义的模块, 程序组件共用的功能) - common.py core: 核心逻辑(与业务相关的逻辑, 购物车的登陆注册转帐取款等) - src.py log: 日志(程序运行产生的关键信息记录) - transaction.log db: 数据(数据库相关的文件) Readme: 读我(软件的介绍, 说明书)

调用业务相关的逻辑应该经过start.py去调取src模块,因此要添加环境变量,而后在start.py中添加代码

# run.py

import sys sys.path.append(r'E:\Python\ATM\core') import src src.run()

虽然添加core可以让start.py调取到src模块,但可不能这么作。由于之后别人做为软件的使用者,下载了这个软件而后使用,但这里添加的环境变量是开发者本身机器上的文件路径,别人使用时和我确定不是同样的文件路径,因此没法使用。这时须要让使用者不管在哪一个路径下都能使用这个软件,因此要添加一个通用的环境变量,具体作法是添加整个项目的根文件夹。可是这里可不能又写我硬盘上的文件路径,而是让程序本身获取整个项目的根文件夹。

# run.py

print(__file__) # 运行
E:/Python/ATM/bin/start.py

__file__能够获取当前执行文件的绝对路径,不一样的机器能够获取不一样的绝对路径。如今须要获取执行文件所在文件夹的父级文件夹,因此能够基于__file__来获取

os模块有个功能能够获取当前执行文件所在的文件夹的路径

# run.py

import os print(os.path.dirname(__file__)) # 运行
E:/Python/ATM/bin

那么基于上面获取父级文件夹就是再使用一个os.path.dirname

# run.py

import os print(os.path.dirname(os.path.dirname(__file__))) # 运行
E:/Python/ATM

这时便获取到了执行文件所在文件夹的父级文件夹,而后添加到环境变量,之后不管这个软件在哪台机器上运行,均可以获得精准的目录

# run.py

import os import sys BASE_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BASE_DIR) from core import src src.run()

有一个标准的写法,是将if __name__ == '__main__': 添加进去

# run.py

import os import sys BASE_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BASE_DIR) from core import src if __name__ == '__main__': src.run()
相关文章
相关标签/搜索