模块
目标:理解模块、包的含义及使用。
模块
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)
除了多了一个包,其他的都差不多(包括 *),这里就不多说了。