模块和库

模块的概念

前面的练习中,我们的例子都是一个Python代码文件就实现了功能。

那是因为现在我们实现的功能都比较简单,一个代码文件就够了。

如果大家进入到企业里面,成为一名苦逼程序猿,就可以发现,我们要开发的系统会复杂的多。需要开发的代码长达上万行,甚至数十万行、数百万行。

这么多的代码放在一个代码文件里显然是不合理的。

项目开发的的代码文件,可能有成百上千个。

不同的代码文件,实现了不同的功能模块,就像一块块积木一样。这些功能模块文件最后合起来,实现了一个完整的软件。

所以,在Python中,一个代码文件代码,也就是一个.py文件,我们也叫它一个模块(Module)。

比如

a.py 文件,我们就可以称之为模块a

b.py 文件,我们就可以称之为模块b

视频讲解


白月黑羽教Python学习视频 - 模块的概念

模块之间的调用

那么模块之间是怎么互相联系的呢?

我们来看一个例子。

几个同事午餐经常一起去饭店聚餐,我们开发一个程序,记录每人的账单,月末可以结算

我们可以把 输入总费用和聚餐人员,计算人均费用 的功能,单独实现在一个模块文件 aa.py, 其内容如下

fee = input('请输入午餐费用:')
members = input('请输入聚餐人姓名,以英文逗号,分隔:')

# 将人员放入一个列表
memberlist = members.split(',') 
# 得到人数
headcount = len(memberlist) 

# 计算人均费用
avgfee = int (fee) / headcount
print(avgfee)

好了,人均费用计算比较简单。

但是我们还需要将每次账单 记录到文件中,这样才好定期结算。

我们可以创建另外的一个模块文件 save.py。 在里面定义一个函数, 该函数实现记录消费信息到文件功能。 其内容如下

def savetofile(memberlist, avgfee):
    with open('record.txt','a',encoding='utf8') as f:
        # 通过列表推导式,产生    人:费用  这样的列表
        recordList = [f'{member}:{avgfee}' for member in memberlist]
        f.write(' | '.join(recordList) + '\n')
注意:目前我们把 save.py 和 aa.py 放在一个目录中

现在我们要在aa 模块里面使用save模块里面的函数 savetofile,该怎么做呢?

方法一

在aa.py里面 通过import关键字导入模块save ,

import save

这样save 就成了模块 aa 里面的的一个变量,这个变量指向的是一个 模块对象

不要感到奇怪,在Python中, 模块 也是一个对象。

这样我们就可以通过save.savetofile 访问到 save模块里面的函数

完整代码如下:

# 导入 save 模块 
# 导入后 save 就成为模块aa中的一个变量,对应一个模块对象
import save

fee = input('请输入午餐费用:')
members = input('请输入聚餐人姓名,以英文逗号,分隔:')

# 将人员放入一个列表
memberlist = members.split(',') 
# 得到人数
headcount = len(memberlist) 

# 计算人均费用
avgfee = int(fee) / headcount
print(avgfee)

# 使用 模块save里面的 savetofile 函数
save.savetofile(memberlist, avgfee)

视频讲解


白月黑羽教Python学习视频 - 代码中导入模块

方法二

还可以在 aa.py 里面 通过 from import 关键字导入其它模块里面的标识符(包括变量名和函数名等)。

导入之后,这些其它模块的变量名和函数名就成为 模块aa 里面 变量名,函数名了。

这样,我们就可以直接使用它们了。

完整代码如下:

# 从 save 模块 导入标识符  savetofile ,
# 导入后 savetofile 就成为模块aa中的一个变量,对应一个函数对象
from save import savetofile

fee = input('请输入午餐费用:')
members = input('请输入聚餐人姓名,以英文逗号,分隔:')

# 将人员放入一个列表
memberlist = members.split(',') 
# 得到人数
headcount = len(memberlist) 

# 计算人均费用
avgfee = fee / headcount
print(avgfee)

# 直接使用 savetofile 函数
savetofile(memberlist, avgfee) 

一些技巧

如果在一个模块文件中需要导多个其它模块,可以分开写导入语句,像这样

import aa
import bb
import cc

也可以一起导入,模块之间用逗号隔开,像这样

import aa, bb, cc

如果我们要从1个模块里导入多个标识符,可以这样

from aa import func1,var1,func2,var2

如果我们要导入1个模块里面的很多个标识符,可以使用*代表所有可导入的标识符(包括变量名,函数名等)

from aa import *

这里 import * 把aa模块所有可以导入的对象全部都导入了。


如果我们需要从两个模块导入函数,恰好这两个函数是同名的,这是我们可以给其中一个起个别名,避免冲突,比如

from save import savetofile
from save2 import savetofile as savetofile2

视频讲解


白月黑羽教Python学习视频 - 导入模块里面的名字

注意点

当一个b模块从a模块导入了名字 var1 后,注意,b模块和a模块 是 各自有一个 名为 var1 的变量 , 而不是两个模块共享一个 名为 var1 的变量。

我们看下面的一个例子

有一个模块文件 a.py,如下

# a.py 
var1 = 1

def changeVar1():
    global var1
    var1 = 2
    print(f'in a: var1 is {var1}')

另外一个模块文件 b.py, 从a中导入了名字 var1 ,如下

# b.py 
from a import var1,changeVar1

changeVar1()
print(f'in b: var1 is {var1}')

当我们执行 b.py 时,显示结果如下

e:\tmp>python b.py
in a: var1 is 2
in b: var1 is 1

发现没有?

虽然 b模块 从 a模块 导入了名字 var1, 但是他们中的var1 是两个不同名字。 在刚刚执行导入时,这两个var1指向同一个对象,但是后来可以通过赋值语句,分别指向不同的数据对象。

将模块放入包中

当我们的项目模块文件特别多的时候,我们还需要将这些模块文件根据功能划分到不同的目录中。

这些放模块文件的目录,Python中把他们称之为为包 (Package)。

包目录里面需要有一个名字为 __init__.py 的初始化文件,有了它,Python才认为这是一个Python包。

通常,这个初始化文件里面不需要什么代码,一个空文件就可以了。

当然你也可以在里面加入代码,当这个包里面的模块被导入的时候,这些代码就会执行。

下面是一个商城产品目录结构的例子(点击这里下载代码zip包)

stock/                        ---   顶层包
        __init__.py           ---   stock包的初始化文件
        food/                 ---   food子包
                __init__.py
                pork.py
                beef.py
                lobster.py
                ...
        furniture/            ---   furniture子包
                __init__.py
                bed.py
                desk.py
                chair.py
                ...
        kitchen/              ---   kitchen子包
                __init__.py
                knife.py
                pot.py
                bowl.py
                ...

最上层的是stock包, 里面有3个子包 food、furniture、kitchen。 每个子包里面的模块文件都有名为stockleft的函数,显示该货物还剩多少件。

如果我们要调用这些模块里面的函数,可以像这样:

import stock.food.beef
# 注意导入的是 stock.food.beef,调用的时候一定要加上所有的包路径前缀
stock.food.beef.stockleft()

我们也可以使用from…import… 的方式,像这样

from stock.food.beef import stockleft
stockleft()

视频讲解


白月黑羽教Python学习视频 - 包

库的概念

如果你写的 模块文件 里面的函数, 实现了通用的功能,经常被其它模块所调用, 我们就可以把这些被调用的模块文件称之为

库是个抽象的概念,里面提供被其它模块调用的功能函数(或者类等等)

Python标准库

Python语言提供了功能丰富的标准库 。 这些标准库把开发中常用的功能都做好了。

我们可以直接使用它们。

而这些标准库里面有一部分被称为内置库,这些库里面的 类型 和方法 无须使用import导入,可以直接使用。

内置类型有:int、float、str、list、tuple等

内置函数前面我们介绍过,可以在Python的官方文档查看到,点击这里查看

比如像 int,str,print,type,len等等



还有些库,需要使用import导入,才能使用。

常见有 sys, os, time, datetime, json等

比如,我们要结束Python程序,就可以使用sys库里面的exit函数

import sys
sys.exit(0)

再比如,我们要得到字符串形式的当前日期和时间,可以使用datetime库

import datetime

# 返回这样的格式 '20160423'
datetime.date.today().strftime("%Y%m%d") 

# 返回这样的格式 '20160423 19:37:36'
datetime.datetime.now().strftime("%Y%m%d %H:%M:%S")

视频讲解


白月黑羽教Python学习视频 - 库的概念

解释器怎么寻找模块文件?

当Python解释器执行

import xxx

或者

from xxx import  yyy

这样的语句时, Python 解释器是到什么地方查找模块文件xxx的?

内置模块

它首先在解释器 内置模块 ( builtin modules ) 中寻找 是否有 xxx。

所谓内置模块,就是内置在Python解释器程序中的模块,它们是用C语言编写,编译链接在解释器里面。

也就是说它们就是解释器的一部分,所以解释器运行时,它们就在解释器里面,无需查找。

比如:time, sys, gc, math, mmap等,就是内置模块

大家可以通过 sys.builtin_module_names的值来查看哪些模块是直接包含在解释器里面的。


sys.path

如果内置模块没有 xxx 这样的名字 ,接下来到什么地方查找?

Python的 sys 库 里面有个属性 path。你可以运行下面的语句看看 sys.path 的内容

import sys
for path in sys.path:
    print (path)

通常可以得到类似下面这样的结果

C:\projects\first
C:\Python\Python36\python36.zip
C:\Python\Python36\DLLs
C:\Python\Python36\lib
C:\Python\Python36
C:\Python\Python36\lib\site-packages

这个 sys.path 是一个列表,这个列表里面包含了一些 路径

当我们import 一个模块时 ,解释器会 依次到 sys.path 包含的目录下去寻找 有没有同名的模块 。

那么, sys.path 里的路径是怎么来的呢?

解释器启动的时候,是从根据下面这些规则 添加路径到 sys.path 列表里的

  • 启动脚本文件所在的目录

    启动脚本就是执行Python程序后面 的参数 脚本文件。

    比如 python first.py 那么这个 first.py 就是 启动脚本文件, 脚本文件所在的目录 会被加到 sys.path 中作为模块搜索路径。


    如果启动命令行,没有指定脚本文件,而是像这样 python ,直接 启动了 python 交互命令行,

    或者 像这样 python -m pytest [...] 通过 -m 参数 执行某个模块,

    那么 当前工作目录 会被加到 sys.path 中作为模块搜索路径。

  • PYTHONPATH 环境变量里的目录

    我们可以在 PYTHONPATH 环境变量里面添加上 多个目录作为 模块搜索路径。 多个目录之间的分隔符根据操作系统而定。 Windows下面是分号,Linux下面是冒号。

    解释器启动的时候 , PYTHONPATH 环境变量里的目录 会被加到 sys.path 中作为模块搜索路径。

  • Python解释器的 缺省安装目录(installation-dependent default)

    比如: Windows下面就是 在 解释器安装目录中的 lib/site-packages 目录。


如果我们自己想添加一些目录,作为模块搜索路径,怎么办呢?

一种方法是:把这些目录加到环境变量 PYTHONPATH 里。

因为Python解释器在启动后,会把 环境变量PYTHONPATH里包含的目录,自动添加到sys.path的路径列表里面。

比如,在windows下面,我们可以用这样的命令添加

set PYTHONPATH=c:\myproject;c:\myproject\mylib

那么解释器启动后, c:\myprojectc:\myproject\mylib 这两个目录就被添加到了sys.path里面。

注意这样的环境变量添加,只在当前命令行窗口有效。重新打开窗口就会失效了。

如果要确保持久有效,可以添加在windows 的 环境变量设置对话框中。

白月黑羽Python3教程


自己想添加一些目录,作为模块搜索路径,还有一种方法:

就是在代码中直接修改sys.path,使用 append 或者 insert方法,把目录直接添加到该列表中。

视频讲解


白月黑羽教Python学习视频 - 解释器在哪里寻找模块文件1


白月黑羽教Python学习视频 - 解释器在哪里寻找模块文件2


安装其他的库

Python 之所以这么流行,就是因为它有太多的优秀的第三方库和框架。

有了这些库,Python才能在各行各业都这么受欢迎。因为这些库极大的提高了开发效率。

要使用这些库,我们需要安装到本地。

在Python中,安装第三方库,通常是使用pip命令。

那些优秀的第三方库,基本都是放在一个叫 PYPI 的网站上

pip命令会从该网站下载这些库的安装包进行安装。

例如,如果我们要安装 requests库,可以执行如下的命令

pip install requests


我们在国内,使用pip 安装的时候,可能由于网络原因,到国外访问 PYPI 会比较慢。

而国内有网站(比如豆瓣)对PYPI 做了镜像备份。

我们可以通过在命令中加上参数 -i https://pypi.douban.com/simple/ ,这样就指定使用豆瓣作为安装包的下载网站。

如下所示:

pip install requests -i https://pypi.douban.com/simple/


如果pip安装库的时候,出现SSL错误,可能是网络对https证书校验的问题,可以改用http协议下载,如下所示

pip install requests -i http://pypi.douban.com/simple/  --trusted-host pypi.douban.com

注意,很多初学者会在python shell里面执行pip命令,像这样

白月黑羽Python3教程

这个命令是一个程序,不是Python语句,要在cmd 命令行中直接执行,像这样

白月黑羽Python3教程

安装好以后,我们就可以使用import去导入这些库并且使用了

import requests
requests.get('http://www.baidu.com')


扫码分享给朋友,一起学更有动力哦






课后练习

去做练习

上一页 下一页