一、什么是模块

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

模块有三种来源:

  1、内置的模块

  2、第三方的模块

  3、自定义模块

模块的四种通用类别:

  1、使用Python编写的代码(.py文件)

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

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

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

二、为何要用模块

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

  2、使用自定义模块的好处是:可以减少代码冗余(抽取我们自己程序中要公用的一些功能定义成模块,然后程序的各部分组件都去模块中调用共享的功能)

三、模块的使用

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

1、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)   #
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) #
print(money) #

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

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

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

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

import spam, os, time

可以为模块起别名(注意:模块名应全为小写)

import spam as sm

print(sm.money)
print(sm.read1)

2、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) #

还可以导入全部模块,但不推荐使用

# 星号代表从被导入模块中拿到所有名字
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()

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

1、直接运行

2、作为模块导入

# 在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

五、模块的搜索路径

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

  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模块了

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

2、添加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

3、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')

 六、软件开发的目录规范

以 “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()

Learning-Python【16】:模块的导入使用的更多相关文章

  1. python 16 模块

    目录 模块 1. 自定义模块 1.1 模块分类 1.2 模块的导入 1.3 import 和 from 1.4 from 模块名 import * 1.5 模块的用法: 1.6 导入路径 2. tim ...

  2. python与模块的导入方式

    今日所得 模块 import from...import... 循环导入 相对导入 绝对导入 软件开发目录规范 模块 模块:是一系列功能的集合体 模块的三种来源:1.内置模块(Python解释器自带的 ...

  3. python之模块的导入

    今天在做一个项目的时候卡在模块导入这个点上了.赶紧回头总结一下 一.被导入的文件和工作的脚本在一个目录下 1.导入一个.py文件里的功能或参数(导入模块) 先看一下目录结构: module里有两个功能 ...

  4. 17.python自定义模块的导入方式

    1.直接用import导入 最后运行main.py可以看到命令行窗口输出了一句:你好,这样就完成了. 2.通过sys模块导入自定义模块的路径path 3.在环境变量中找到自定义模块 这个方法原理就是利 ...

  5. python第三方模块的导入

    模块搜索路径 当我们尝试加载一个模块时,Python会在指定的路径下搜索对应的.py文件,如果找不到,就会报错: >>> import module1 Traceback (most ...

  6. 万恶之源 - Python 自定义模块

    自定义模块 我们今天来学习一下自定义模块(也就是私人订制),我们要自定义模块,首先就要知道什么是模块啊 一个函数封装一个功能,比如现在有一个软件,不可能将所有程序都写入一个文件,所以咱们应该分文件,组 ...

  7. python 浅析模块,包及其相关用法

    今天买了一本关于模块的书,说实话,模块真的太多了,小编许多也不知道,要是把模块全讲完,可能得出本书了,所以小编在自己有限的能力范围内在这里浅析一下自己的见解,同时讲讲几个常用的模块. 这里是2018. ...

  8. Python自定义模块

    自定义模块 自定义模块(也就是私人订制),我们要自定义模块,首先就要知道什么是模块 一个函数封装一个功能,比如现在有一个软件,不可能将所有程序都写入一个文件,所以咱们应该分文件,组织结构要好,代码不冗 ...

  9. python之模块、包的导入过程和开发规范

    摘要:导入模块.导入包.编程规范 以My_module为例,My_module的代码如下: __all__ = ['name','read'] print('in mymodule') name = ...

  10. python中根据字符串导入模块module

    python中根据字符串导入模块module 需要导入importlib,使用其中的import_module方法 import importlib modname = 'datetime' date ...

随机推荐

  1. CF3A Shortest path of the king

    The king is left alone on the chessboard. In spite of this loneliness, he doesn't lose heart, becaus ...

  2. vue里的v-show和v-if

    v-show:false            对应的是display:none:不移除dom元素.对网页渲染性能更好,适应于频繁的操作该dom的显示隐藏. v-if:  false       对应 ...

  3. js的小知识7

    1.函数都有返回值...... 而方法的本质也是函数,所有也有返回值. Document.getElementById()返回的是获取的标签 getElementByClassName()和getEl ...

  4. 用友U8存货分类通过DataTable生成EasyUI Tree JSON

    <%@ WebHandler Language="C#" Class="InventoryClass" %> using System; using ...

  5. python全栈开发 * 29知识点汇总 * 180712

    29 正则表达式 re模块一.正则表达式官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”, 这个“规则字符串”用来表达对字 ...

  6. JavaScript基础知识(字符串的方法)

    字符串的方法 1.字符串: 在js中被单引号或双引号包起来的内容都是字符串: var t = "true"; console.log(typeof t);// "stri ...

  7. 数组copy

    数组copy(推荐用法) System.arraycopy的用法 int[] src = {1,3,5,7,9,11,13,15,17}; int[] dest = {2,4,6,8,10,12,14 ...

  8. Linux命令 sed

    一. 以行为单位进行操作. d:删除 $ nl passwd | sed '2,5d'  # 删除第2~5行 $ nl passwd | sed '2d'  # 删除第2行 $ nl passwd | ...

  9. 1.7Oob方法的作用

    public class Exse2 { public static void main(String[] args) { sumIntLong(10,15); sumIntLong(20,30); ...

  10. mybatis BindingException: Invalid bound statement (not found)

    错误截图 解决措施 此异常的原因是由于mapper接口编译后在同一个目录下没有找到mapper映射文件而出现的. 通常我们在配置SqlSessionFactory时会有如配置 <!-- 配置Sq ...