模块

目标:理解模块、包的含义及使用。

模块

Python 模块,其实就是一个包含 Python 代码的 .py 文件,你可以理解它是给 py 文件取的一个高大上的名字。

前面的代码中,无论我们写多少代码,如果要一起使用,都需要写到一个文件中。

那如果以后我们要写项目,代码非常非常多,也写到一个文件中,那得写上万行,那这个文件读起来多难受。

为了解决这样的问题,Python 允许模块之间的联合使用,接下来就学习一下模块之间怎么联合使用。

模块导入

在 Python 中把其他模块拿过来使用称为模块导入,导入模块需要使用 import 关键字,而 import 导入模块则有两种方法:

导入模块里的所有内容

import 模块名

导入模块里的指定内容

from 模块名 import 功能

比如,现有两个 py 文件,分别为 mk1.py 和 mk2.py。mk1.py 中的代码如下:

mk1.py

def test():

print("test")

name = 'mk1'

其中有一个函数和一个变量。

在 mk2.py 文件中导入 mk1.py 中的内容:

mk2.py

导入模块里的所有内容

import mk1

print(mk1.name) # mk1

mk1.test() # test

因为导入的是所有的内容,所以直接可以使用,但是需要通过模 块名.成员 的方式调用。

为什么呢?

因为一个 py 文件中可以导入的模块不止一个,如果其他模块中有同样的函数或变量,那不就冲突了吗,所以需要通过 模块名.成员 来调用。

导入模块里的指定内容

这种方式就是需要什么就导入什么,最后可以直接使用,而不需要再通过 模块名. 来调用。

from mk1 import name

from mk1 import test

print(name) # mk1

test() # test

如果你要导入的很多,可以使用通配符 * 代替具体名称,它表示导入所有:

from mk1 import *

print(name)

test()

这种情况下,不需要再写具体的名称。

* 默认代表所有,但是本质上是代表模块中的 __all__ 列表中的值,只有在其中才会被 * 导入。

模块分类

Python 发展了这么久,很多功能都是我们经常使用的。对于这种经常使用的功能,是不是应该保留起来以便以后使用呢?

我都能想到的东西,他们肯定想到了,所以就产生了非常非常多别人提供的模块以供我们使用。对这些由不同的人提供的模块进行分类,可以分为:

自定义模块:自己定义的模块

第三方模块:第三方开发者开发的模块,并发布在 Python 的包管理器 pip 的仓库中

内置模块:Python 自带的模块,你不需要进行任何安装就可以直接使用

这些模块也不需要特地记忆,自定义的不用多说,内置的需要导入,而第三方需要安装(从仓库拿出来)。不管哪种,我们都是导入使用,所以只管用好就行,不用问来处。

内置模块演示

Python 内置模块有很多,比如常用的 math、os、sys 等。下面用用看:

import math

# 整除(商的整数部分)

print(math.floor(5 / 2)) # 2

# 向上取整(小于等于x的最大整数)

print(math.ceil(2.3)) # 3

# 向下取整(大于等于x的最小整数)

print(math.floor(2.3)) # 2

在后面会经常用到 Python 的内置模块,到时候用到什么模块再说什么模块。

第三方模块

第三方模块不像内置模块安装了 Python 就可以使用,它就像独立显卡,需要你单独安装才能使用。

安装的方式:

pip install 第三方模块名称

不过一般会使用 -i 指定镜像源下载,比如下载 Python 的打包库 - pyinstaller:

pip install -i https://mirrors.aliyun.com/pypi/simple/ pyinstaller

这个库可以帮我们把写的 Python 程序打包成 exe 可执行文件,方便执行。

什么是镜像源?

前面说到第三方模块会放到仓库中,pip 仓库默认是在国外的,由于种种原因,可能会很慢,甚至下载失败。

所以国内也建了很多仓库,方便我们使用,直接从国内拿就快很多(就像京东仓库,不打广告哈)。

清华大学 :https://pypi.tuna.tsinghua.edu.cn/simple/

豆瓣:http://pypi.douban.com/simple/

中国科学技术大学 : https://pypi.mirrors.ustc.edu.cn/simple

阿里:https://mirrors.aliyun.com/pypi/simple/

打包

打包需要在控制台输入命令:

pyinstaller -F py文件路径

比如刚刚创建的 mk1.py 文件:

pyinstaller -F mk1.py

打包好后,会在该文件所在目录生成 dist 目录,可执行文件就在它下面。

但是需要注意,你代码怎么写的,它就怎么跑,你没有输出语句它自然也不会输出。而且,当程序执行完成后,就会自动结束。

这个不多说,就做演示用,不是学习的重点。

__name__ 变量

有一个问题,如果在 mk1 文件中,本身就将 name 变量给输出了,那在 mk2 中导入 mk1,并且再次输出了 name 变量,那控制台会输出几次?

mk1.py

def test():

print("test")

name = 'mk1'

print(name)

mk2.py

import mk1

print(mk1.name)

答案,会输出两次。

按照我们的需求,应该是只需要一次的,那怎么解决这样的问题呢?

自己写去,不给你用

只打印一次

不管是哪个好像都不太好,就说第二个,因为这个模块我自己也需要使用,或者说测试,那我肯定要用。调用的人也不知道你用没用,所以他也要用,这就没办法了。

来看看两种调用的区别:

自己用

自己用的时候,是通过鼠标右键点击 Run xxx 来运行自己这个文件(mk1)。

作为模块被调用

作为模块被调用,Run 的是调用者(mk2),并没有运行被调用的模块。

不知道这样说大家能不能理解。

在 Python 中,如果 Run(运行)的是自己,则他会认为你是程序的入口,非常重要(main),此时他会给你一个 “工牌标识” 叫 __main__,一个程序都只有一个的大佬。

如果你只是被人使用的,那程序认为你没那么重要,给你个 “工牌标识” 直接写你的名字就完了(文件名称)。

这个 “工牌标识” 其实就是一个全局变量 __name__,被赋予了不同的值而已。

基于这个机制,那我们就可以通过它来判断是否执行某些代码。比如:

mk1.py

def test():

print("test")

name = 'mk1'

if __name__ == '__main__':

print(name)

mk2.py

import mk1

print(mk1.name)

模块:代码太多了,分成不同的文件保存;

包:文件太多了,分成不同的文件夹保存;

所以包,其实就是一个文件夹,用来管理相关的模块和包(文件夹下面还可以有文件夹)。

所以,对于包就没有太多需要说的了,下面直接开始使用。

创建包

在 PyCharm 中创建包和文件夹还是有些微区别的,创建包是这样的:

区别在哪?

包在创建完成后,会自动生成一个 __init__.py 文件,不过里面什么都没有。

这个文件的特点是,当导入这个包时,会首先执行一次这个文件中的内容(只要导入就会用,不管用没用到它里面的模块)。

导入包

包的导入和模块差不多,不过包又多了一层:

导入包

import 包名称

此时,只是单独的导入了包,相当于导入了 __init__.py 文件,模块暂时还无法使用。

导入包里的指定内容

from 包名称 import 模块名称

比如创建了 pag1 包,_init_.py 中的代码如下:

print("pag1 包")

package_name = "pag1 包1"

mk2.py

import pag1

输出:

pag1 包

运行的是 mk2.py,可以看见导包就执行了 _init_.py 下的代码。

包下的模块

现在在包下创建两个模块:pag_model1.py 和 pag_model2.py,代码分别如下:

pag_model1.py

name = "pag_model1.py"

pag_model2.py

name = "pag_model2.py"

如果要使用到包下模块中的内容,在导包的情况下直接这样调用可以吗?

import pag1

print(pag1.pag_model1.name)

结果报错:

AttributeError: module 'pag1' has no attribute 'pag_model1'

它说 pag1 模块没有 pag_model1 属性,所以,不行。

如果要使用包下的模块,可以用以下两个方式:

__init__.py 文件中引入模块

# 导入包

from .pag_model1 import *

from .pag_model2 import *

print("pag1 包")

package_name = "pag1 包1"

mk2.py 文件

import pag1

print(pag1.pag_model1.name)

print(pag1.pag_model2.name)

注意,在使用的时候,还是要通过包确定模块,从而避免绝大多数的冲突。

导入模块

这种方式不需要在 _init_ 文件中做什么操作,可以直接导入使用:

from pag1 import pag_model1

from pag1.pag_model2 import name

print(pag_model1.name)

print(name)

除了多了一个包,其他的都差不多(包括 *),这里就不多说了。