1. package & modules
在进行python编程时,往往编写大量的代码,一个文件下有过多的代码,一旦要修改或者查找,工作量比较大,不利于其他人的阅读。为了更好的对python进行代码管理,引入的包和模块的概念,将代码按照不同的功能进行整理,来提高代码的可读性和代码质量。
1.1 包和模块的定义
python 中的包和模块,首先是按照代码的功能进行整理,将相似的功能的代码/大量代码整理到一起方便管理。 |
- 模块:python中的一个源代码文件就是一个模块,模块名就是文件名称,每个模块中都有封装的变量/函数/类型,可以被其他的python模块通过 import 关键字引用重复使用。
- 包:python 中的文件夹就是一个包,标准的 python 包是一个文件夹,并且文件夹中有一个包声明文件__init__.py,包声明文件就代表包本身,可以将大量功能相关的 python 模块包含起来统一管理,同样也可以用 import 关键字引入重复使用。
1.2 包和模块的复用
python 中,将代码封装成包和模块,最主要的目的是提高代码的复用性,可以被其他模块引入使用
1.2.1 import 和 from . . import
包和模块引入,通常有两个关键语法:
- import 包 / 模块
- from 包 / 模块 import 具体对象
(1)import 方式
import 引入包和模块会自动从site-packages中,系统环境变量 PYTHONPATH 中,以及系统的sys.path 路径中查询是否存在该名称的包和模块
# 直接引入模块
import utils
# 使用过程中,必须通过 模块名称.变量名称/模块名称.函数名称/模块名称.类型名称 使用
# 使用模块中的变量
print(utils.msg)
# 使用模块中的函数
utils.test()
# 使用模块中的类型
p = utils.Person()
print(p)
(2)from . . import 方式
from . import xxx # 从当前模块路径下,引入xxx模块 from . . import xxx # 从当前模块的父级路径下,引入xxx模块【】 from pkg import module # 从pkg 包中引入一个模块 module 【推荐***】 from pkg import msg , test, # 从指定的模块中直接引入变量/函数/类型 |
备注1:from . . import 方式主要是针对包结构而特定的代码引入方式,首先要非常明确代码的组织结构才能正确使用from import 语法进行代码的引入和复用 |
通过上述代码,可以看到 form import 语法区分为两种操作
|
1.2.2 模块的引入
1.2.2.1 模块的相对引入
相对引入本身是相对于当前正在操作的文件的路径
-
同一级路径使用符号:.
- 上一级路径使用符号:..
#相对引入:从当前文件夹/目录/路径中,查询并引入utils模块
from . import utils
# 使用模块的变量
print(utils.msg)
# 调用执行函数
utils.test()
# 使用模块中的类型
p = utils.Person()
print(p)
1.2.2.2模块的绝对引入
绝对引入操作方式比较直接,从最外层的源头直接开始操作;
# 绝对引入
import utils
# 使用另外一个模块中的变量 (没有全局/局部变量的概念)
print(utils.msg)
# 使用另外一个模块的函数
utils.test()
# 使用另一个模块的类型
p = utils.Person()
print(p)
模块的相对引入和绝对引入的使用情况:
1. 如果开发应用软件/内部项目:产品 |
2. 如果开发的工具软件:工具模块~pymysql/pygame 第二种引入方式使用较多,from pygame import K_A ~当前开发的包,可能会被不同的项目引入使用。 |
1.2.2.2模块的不同引入
# 1. 引入方式:直接一个模块[店家推荐]
import utils
# 使用过程中,必须通过 模块名称.变量名称/函数名称/类型名称 使用
print(utils.msg)
utils.test()
p = utils.Person()
# 2. 引入方式:通过模块,引入模块中的变量/函数/类型
from utils import msg, test, Person # 不推荐:代码的可读性较低
# n行代码
# 分辨不清msg/test/Person 是当前模块的数据,还是其他模块的数据
print(msg)
test()
p = Person()
1.2.3 包的引入
1.2.3.1 包的相对引入与绝对引入
# 1. 绝对引入:直接引入包中的模块
# pycharm开发工具:会自动搜索site-packages/PYTHONPATH/sys.path路径下查询对应的模块
# python解释器(CPython):自动搜索[三个标准路径+当前路径]
import modules.tools
# # 使用模块中的变量
print(modules.tools.msg)
# # 使用模块中的函数
modules.tools.test()
# # 使用模块中的类型
a = modules.tools.Author()
print(a)
# 2. 相对引入方式:包含了相对路径的操作
from .modules import tools
#使用模块中的变量
print(tools.msg)
# 使用模块中的函数
tools.test()
# 使用模块中的类型
author = tools.Author()
print(author)
包的相对引入和绝对引入
1. 相对于当前正在开发的内部项目,使用相对路径引入包进行操作 |
2. 相对于要发布到网络上的工具模块/在公司多个项目中使用的公共模块,使用绝对路径引入包的方式进行操作 from modules import tools |
1.2.3.2 包的引入操作
# 1. 直接引入包: 使用包中的模块~必须在当前包的__init__.py文件中主动引入【不推荐】
# import modules
print(modules.tools.msg)
print(modules.utils.msg)
modules.tools.test()
# 2. 直接引入包->模块【推荐:代码可读性较高】
import modules.tools #可以这样引入
print(modules.tools.msg)
import modules.tools as t # 【店家推荐:**】
print(t.msg)
# 熟练
from modules import tools # 【店家推荐:***】
print(tools.msg)
# 3. 包的引入第三种方式【不推荐】
from modules.tools import msg, test, Author
# 原因:可读性很差[变量的定义造成了误读:分辨不清这个变量是当前模块还是其他模块]
print(msg)
msg = "hello"
print(msg)
1.3 模块和包
1.3.1 模块
一个标准模块的定义方式:
# 引入系统标准模块
# 引入第三方模块
# 引入自定义开发模块
# 声明定义变量
# 声明定义函数
# 声明定义类型
1.3.2 包:__all__
默认情况下,包中所有的模块都是可以直接导入的,同样为了使用方便,可以通过通配符的方式来一次引入包中所有指定的模块
__all__属性就是用于模糊导入的特殊魔法属性,值是一个包含模块名称的列表,主要声明在__init__.py 文件中,用于定义可以用通配符的方式引入的模块。
# modules/__init__.py
__all__ = ['module', 'manager', 'menus']
此时可以在main.py中通过通配符的方式将__all__中包含的模块一次性导入
from users import *
#相当于
from users imoprt module, manager, menus
1.3.3 安装和卸载第三方模块
- 安装第三方模块的两种方法:
a)pip install <module_name>
b) 下载<module_name>压缩包-> 解压-> python setup.py install
pip list 查看当前解释器中安装的所有第三方模块
- 卸载第三方模块的方法:
pip uninstall <module_name>
通过 pip uninstall <module_name> 卸载一个模块,一般情况下通过pip install <module_name>安装能卸载成功。但是通过python setup.py install 这样安装的模块有可能卸载失败,需手工进入site-packsge/文件夹中,直接删除要卸载模块对应的文件夹。 |
1.4. 自定义包的发布
1.4.1. 本地发布
在我们已经开发好的一个包文件夹下,创建一个 python 模块: setup.py
模块中定义如下内容
# 引入构建包信息的模块
from distutils.core import setup
# 定义发布的包文件的信息
setup(
name="py1808tools", # 发布包的名称
version="1.00.001", # 发布的包的版本序号
description="我的测试包", # 发布包的描述信息
author="小羊咩咩", # 发布包的作者信息
author_email="2674635186@qq.com", # 作者联系邮箱信息
py_modules=['__init__', 'download', 'engine', 'modules', 'pipelines', 'setup', 'tools', 'utils'] # 发布的包中的模块文件 列表
)
使用MANIFEST.in打包非py的静态资源
具体格式和参数参考https://docs.python.org/2/distutils/sourcedist.html
include HISTORY.rst
include MANIFEST.in
recursive-include image *.png
recursive-include music *
recursive-include plane/font *.ttf
recursive-include plane/image *
recursive-include plane/image1 *
recursive-include plane/sound *.mp3 *.ogg *.wav
执行当前程序包文件的构建操作命令:按照标准格式组织包中的所有数据文件
python setup.py build |
REMARK:构建完毕的文件 可以通过 python setup.py install 命令直接当成第三方模块进行安装 |
执行命令进行包的打包发布
python setup.py sdist |
REMARK:打包的文件,主要是方便进行网络传输,打包之后会在 dist 中创建包含包中所有信息的 tar.gz 压缩包文件; |
1.4.2. 网络发布
首先,进入 http://pypi.org 网站上,注册一个自己的账号吧!
其次:你已经准备好打包好的 python 程序包,并在包中准备好了 setup.py 文件
接下来:将是激动人心的时刻!
首先安装第三方模块:twine,用于上传我们打包的项目文件
pip install twine
上传项目
twine upload dist/*
1.5. 关于 Main 主方法
对比其他语言,其他语言都有一个特殊的程序入口,main方法/main函数
python 中最小的执行单元:代码块~不存在main方法/函数
为了程序的完善性~模拟了一个main方法的操作
python文件 -> python模块->模块名称__name__
默认情况下,模块的名称__name__就是文件的名称,如果当前python 文件是执行文件【python xxx.py】
————> __name__ ="__main__"
每个python模块都有一个魔法属性 __name__,指代当前模块的名称
为了保证被引入模块的测试代码不至于在import时执行导致错误,一般情况下,每个模块中的测试执行代码,都需要包含在一个选择判断中【main函数/方法】
if __name__ == "__main__":
当前模块作为运行模块 要执行的代码
- 测试代码
- 程序入口
面试题:能简单说说Python 中的main函数/方法
- python 中没有所谓的main函数/方法
- python 中为了保证测试代码的正确性和程序入口的唯一性,通过选择结构模拟一个main函数/方法,主要通过python模块的魔法属性__name__ 也就是当前模块的名称属性进行操作,默认情况下名称属性就是当前文件模块的名称,但是如果作为模块的话该名称属性的值就会是__main__,所以通过__name__是否是__main__的判断完成了main函数的模拟。
- 扩展加分:一般情况下我们在开发时,这个模拟main函数/方法中,会有两个作用,第一个作用用来编写测试代码;第二个作用用来规范程序入口。