python 历险记(五)— python 中的模块
前言
这次我们继续探险,来搞定 python 中的模块(module)。兵马未动,粮草先行,开工之前先看看基础是否补齐了_。
基础
模块的概念你一定不会陌生吧,这是一个非常宽泛的概念,在各行各业都会用到。这里我们涉及的只是软件中的模块概念。说到模块,就得先了解下模块化程序设计的概念。(如果您对模块化程序设计的概念已经烂熟于心,尽可以略过,而直接跳到下一节)
模块化程序设计
模块化程序设计是指进行程序设计时将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个确定的功能,并且在这些模块之间建立必要的联系,通过模块的相互协作完成整个功能的程序设计方法。
——百度百科
举个例子,假如我们要造一辆汽车,就可以先将汽车分成四个部件:发动机、底盘、电气设备、车身。每个部件都是一个模块,都会完成自己专属的功能,同时也预留了和其他部件配合的接口。当这几部分建造完成时,就可以组合在一起,从而完成了整个汽车的建造。
类比一下,汽车就是整个程序,而像发动机,底盘等就是程序中各个小的模块。只有当各个模块正常工作,并且暴露的接口和其他模块的接口完全契合,才能组合成一个完整且正常工作的程序。
模块化有哪些好处?
当然,如果不将程序分解成一个个独立的部分,而是整个一大坨,也能够完成所要的功能。那么为什么教科书还有实际使用中都会提倡模块化程序设计?这样做有什么好处呢?
控制程序设计的复杂度
不知你看过《代码大全》没有,里面有一句非常著名的格言:软件的首要使命就是管理复杂度。完整的软件功能复杂度是非常高的,如果不使用有效的方法加以管理,很可能会陷入复杂的泥潭中不可自拔。而将程序分解成模块,则会将整体功能的复杂度有效的下分到各个模块中。每个模块只要能够管理好自己的复杂度就可以了。
提高代码的重用性
还是以造汽车为例,假设我造了一个很牛的发动机,多款车型都可以使用它。程序设计也一样,如果一个模块能够完成特定的功能,且与父程序耦合度较小,多个程序都可以使用它。
易于维护和扩展
小 A 写了一个程序,并将各个部分划分的非常明确,再加以人性化的函数命名和注释。即使有一天小 A 离职了,小 B 要接过来维护以及在此基础上再开发新的功能也不难。
既然模块化就这么多好处, 强大的 python 当然也会吸收这个优秀的设计思想,并且在语言中有所体现,那就是 python 的模块(module)。
什么是 python 中的模块?
先来看一个示例:
创建 python 文件 a.py,并在文件中定义函数
sum
def sum(a, b):
return a + b
创建 python 文件 b.py, 并调用
sum
函数from a import sum print(sum(1, 2)) # 3
文件 a.py
就是一个模块(module),b.py
就是一个主模块(main module)。
在 b.py
中有这么一句 from a import sum
,是指将模块 a 中的 sum
函数导入到当前模块中。我们定义的文件名是 a.py ,而模块名就是去掉后缀后得到的 模块 a。那么能不能再多导入几个函数或者导入模块 a 的全部函数呢?当然可以,这个我们后面讲。
调用模块时,通过文件名就可以确定模块的名字,那么在模块(module)内部,能知道自己姓甚名谁吗?还真能。
每个模块都有一个全局变量 __name__
,它就是模块的名字。上面 a.py 的内容不变,修改下 b.py
的内容。
import a
print(a.__name__) # a
print(a.sum(1, 2)) # 3
来,一起总结下:
- python 模块(module) 是指包含 python 定义(包括 类,函数,变量)和语句的文件(.py做后缀)
- 模块名就是模块文件名称去掉.py 后缀
- 在模块内部,可以通过全局变量
__name__
得到模块名称
引入模块有几种方式?
要导入模块并调用,前提要导入的 python 模块中有料(函数,变量,class)才可以。先来定义一个 python 模块 calc
def plus(a, b):
return a + b
def subtract(a, b):
return a - b
再创建一个 main.py 文件,在其中做引入操作。okay,准备好了,那我们来逐个看下可以引入模块的方式吧。
引入整个模块,调用时需要加上模块名
import calc print(calc.plus(1, 2)) # 3 print(calc.subtract(2, 1)) # 1
引入模块特定的函数或变量,调用时无需加模块名
from calc import plus, subtract print(plus(1, 2)) # 3 print(subtract(2, 1)) # 1
引入整个模块,调用时无需加上模块名
from calc import * print(plus(1, 2)) # 3 print(subtract(2, 1)) # 1
引入整个模块,并对模块重命名,调用时加上重命名后的模块名
import calc as calculator print(calculator.plus(1, 2)) # 3 print(calculator.subtract(2, 1)) # 1
引入模块特定的函数或变量,并对其重命名,调用时无需加模块名
from calc import plus as add, subtract as sub print(add(1, 2)) # 3 print(sub(2, 1)) # 1
数一下,一共是 6 种方式,归纳一下就是
from
,import
,as
,*
这些符号的组合而已。
模块的查找顺序
在上几篇文章中已经用了如 os,shutils,json 等多个模块 ,这些模块都是 python 的内置模块。相比之下,我们刚才使用的 calc
模块就是自定义模块。
假设我们使用 import calc
导入 calc
模块, python 在启动时按照什么样的顺序来查找这个模块呢?
- 先查找内置(built-in)模块中有没有,如果没有转到 2
- 查找
sys.path
变量指定的路径下有没有, 有的话就使用,没有就报错
sys.path
变量中存储了那些路径呢?
当前运行的 python 脚本所在的目录
环境变量
PYTHONPATH
中的路径,它和 shell 环境变量PATH
差不多这个变量可以使用 python 脚本在运行时修改它
默认的 python 安装包的路径
想要看下你的电脑当前 sys.path
有哪些路径吗?运行下面代码就可以
import sys
print(sys.path)
查找模块的顺序是从前向后,只要查到就使用,因此这个变量存储路径的顺序很重要。
模块中包含执行语句的情况
如果引入的模块中包含一些执行语句,那么在导入模块时这些语句就会执行。但是即使同样的模块被导入了两次,这些语句也只能执行一次。
来看下面的例子, 定义 calc
模块
print('I am clac module')
def plus(a, b):
return a + b
def subtract(a, b):
return a - b
并且在 main.py 中定义导入两次 calc
模块的函数
from calc import plus
from calc import subtract
print(plus(1, 2))
print(subtract(1, 2))
结果是 'I am clac module'
只会被打印一次。
用 dir() 函数来窥探模块
dir()
函数是 python 的内置函数,可用来获取模块的属性,方法等信息,当我们刚接触一个模块,不清楚它由哪些有用的属性和方法时,就可以用 dir()
来一探究竟。
以常用的 json模块 为例,我们来展示下它的属性和方法
import json
print(dir(json))
# ['JSONDecodeError', 'JSONDecoder', 'JSONEncoder', '__all__', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_default_decoder', '_default_encoder', 'codecs', 'decoder', 'detect_encoding', 'dump', 'dumps', 'encoder', 'load', 'loads', 'scanner']
其中以双下划线开头的变量,如 __name__
并非是模块自己定义的,而是与模块相关的默认属性。
如果我想查看当前模块内的所有属性和方法呢?去掉 dir()
函数的参数就可以。拿上节的代码为例来看下。
from calc import plus
from calc import subtract
print(plus(1, 2))
print(subtract(1, 2))
print(dir())
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'plus', 'subtract']
我们会看到 calc 模块的 plus
和 substract
方法也展示了出来,那么 dir
函数究竟是从哪里获取的数据,背后的机理是什么呢?
其实每个模块内部都有一个子集的私有符号表,它就是模块内所有函数和方法共享的全局符号表。当模块 B 导入模块 A 时,就会把要导入的模块 A 或者特定的方法,属性放置到模块 B 的全局符号表中,dir()
函数也就是从模块中的全局符号表中获取出的值。
python 的内置模块有哪些?
python 的内置模块太丰富了,几乎可以满足我们日常的任何需求。既然有轮子就在那里,而且这轮子又快又好,又何必再造轮子呢?快来看下它的常用内置模块有哪些。
模块名 | 功能简述 |
---|---|
calendar | 与日期相关 |
datetime | 处理日期和时间 |
csv | 读写 csv 文件 |
json | 读写 json 格式数据 |
collections | 提供有用的数据结构 |
io | 处理 I/O 流 |
os | 基本的操作系统函数访问 |
shutil | 高级文件处理 |
tempfile | 创建临时文件和目录 |
logging | 日志功能 |
random | 生成伪随机数 |
copy | 复制数据相关 |
codec | 编解码 |
re | 正则表达式 |
uuid | 全局唯一标识符 (UUID) |
multiprocessing | 运行多个子进程 |
threading | 线程 |
concurrent | 异步 |
argparse | 解析命令行参数 |
atexit | 注册在程序退出时调用的函数 |
signal | 处理 POSIX 信号 |
光常用的模块就这么一大堆,确实是很难都记住,记不住也没关系,当需要用到的时候随用随查就可以了。
结语
本篇中主要介绍了模块化的定义,引入模块化的方式,模块的查找顺序,常用的内置模块简介等内容,通过使用模块化,能够更好的实践模块化程序设计的思想。但本篇并没有涉及 package 的概念,会在后续章节讲述。
下篇会讲述 python 中正则表达式,敬请期待。
参考文档
系列文章列表
- python 历险记(一)—String,集合(List,元组,Dict)
- python 历险记(二)— python 的面向对象
- python 历险记(三)— python 的常用文件操作
- python 历险记(四)— python 中常用的 json 操作
python 历险记(五)— python 中的模块的更多相关文章
- 【Python基础】07_Python中的模块
1.模块的概念 模块 就好比 工具包,要想使用这个工具包中的工具,就需要 导入import 这个模块 每一个以扩展名 .py 结尾的 Python源代码文件 都是一个 模块 在模块中定义的 全局变量. ...
- python导入上级目录中的模块
python导入同级别模块很方便: import xxx 要导入下级目录页挺方便,需要在下级目录中写一个__init__.py文件 from dirname import xxx 要导入上级目录,可以 ...
- python第五十三课——time模块
1.time.datatime.calendar模块的引入讲解(重视) Unix时间戳(timestamp):返回的是数值类型数据(float值), 概念:记录了从1970年00点00分00秒至今的秒 ...
- Python import 指定目录中的模块
#coding=utf-8 import os,sys sys.path.append('test') # 下级目录(text) parentdir = os.path.dirname(os.path ...
- Python基础(五) python装饰器使用
这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 # -*- coding:gbk -*- '''示例1: 最简单的函数,表示调用了两次 ...
- Python学习(五) Python数据类型:列表(重要)
列表: list是一组有序项目的数据结构. 列表是可变类型的数据,列表用[]进行表示,包含了多个以","分隔的项目. list=[] type(list) //<type ' ...
- Python第五章__模块介绍,常用内置模块
Python第五章__模块介绍,常用内置模块 欢迎加入Linux_Python学习群 群号:478616847 目录: 模块与导入介绍 包的介绍 time &datetime模块 rando ...
- 简学Python第五章__模块介绍,常用内置模块
Python第五章__模块介绍,常用内置模块 欢迎加入Linux_Python学习群 群号:478616847 目录: 模块与导入介绍 包的介绍 time &datetime模块 rando ...
- python 历险记(六)— python 对正则表达式的使用(上篇)
目录 引言 什么是正则表达式? 正则表达式有什么用? 正则表达式的语法及使用实例 正则表达式语法有哪些? 这些正则到底该怎么用? 小结 参考文档 系列文章列表 引言 刚接触正则表达式,我也曾被它们天书 ...
随机推荐
- pymongo学习第1篇——增删改查
参考文档: 1.https://docs.mongodb.org/getting-started/python/ 2.http://api.mongodb.org/python/current/api ...
- 去除html代码中的标签
public static String htmlText(String inputString) { String htmlStr = inputString; //含html标签的字符串 Stri ...
- StormUI详解
StormUI由Cluster Summary,topology summary,supervisor summary,Nimbus Configuration四部分组成,如下图所示: Cluster ...
- webpack快速入门——CSS进阶:消除未使用的CSS
使用PurifyCSS可以大大减少CSS冗余 1.安装 cnpm i purifycss-webpack purify-css --save-dev 2.引入glob,因为我们需要同步检查html模板 ...
- 【PaddlePaddle系列】报错解决方法合集 (不定时更新)
1.PaddlePaddle使用CPU时正常运行,但是使用GPU时却报出一堆错误信息,节选如下: paddle.fluid.core.EnforceNotMet: enforce allocating ...
- TapTap推广统计逻辑
当我们在Taptap上访问某款游戏时,比如https://www.taptap.com/app/34762,taptap会记录下这次访问,它是怎么做的呢. 首先,用记事本打开这个网址,在head部分看 ...
- Linq基础知识之延迟执行
Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行,也就是在enumerator的MoveNext()方法被调用的时候执行,大说数Linq查询操作实 ...
- (win 7)使用puma以后,重启rails server报错: in `trap': unsupported signal SIGCHLD (ArgumentError)
如图: 解决方案: 把config/puma.rb 文件中的 workers Integer(ENV['WEB_CONCURRENCY'] || 2) 改成 workers Integer(ENV[' ...
- android 使用lint + studio ,排查客户端无用string,drawable,layout资源
在项目中点击右键(或者菜单中的Analyze),在出现的右键菜单中有“Analyze” --> “run inspaction by Name ...”.在弹出的搜索窗口中输入想执行的检查类型, ...
- wp 去除google字体加载
add_filter('gettext_with_context', 'disable_open_sans', 888, 4 ); function disable_open_sans( $trans ...