python 历险记(五)— python 中的模块
前言
这次我们继续探险,来搞定 python 中的模块(module)。兵马未动,粮草先行,开工之前先看看基础是否补齐了_。
基础
模块的概念你一定不会陌生吧,这是一个非常宽泛的概念,在各行各业都会用到。这里我们涉及的只是软件中的模块概念。说到模块,就得先了解下模块化程序设计的概念。(如果您对模块化程序设计的概念已经烂熟于心,尽可以略过,而直接跳到下一节)
模块化程序设计
模块化程序设计是指进行程序设计时将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个确定的功能,并且在这些模块之间建立必要的联系,通过模块的相互协作完成整个功能的程序设计方法。
——百度百科
举个例子,假如我们要造一辆汽车,就可以先将汽车分成四个部件:发动机、底盘、电气设备、车身。每个部件都是一个模块,都会完成自己专属的功能,同时也预留了和其他部件配合的接口。当这几部分建造完成时,就可以组合在一起,从而完成了整个汽车的建造。
类比一下,汽车就是整个程序,而像发动机,底盘等就是程序中各个小的模块。只有当各个模块正常工作,并且暴露的接口和其他模块的接口完全契合,才能组合成一个完整且正常工作的程序。
模块化有哪些好处?
当然,如果不将程序分解成一个个独立的部分,而是整个一大坨,也能够完成所要的功能。那么为什么教科书还有实际使用中都会提倡模块化程序设计?这样做有什么好处呢?
控制程序设计的复杂度
不知你看过《代码大全》没有,里面有一句非常著名的格言:软件的首要使命就是管理复杂度。完整的软件功能复杂度是非常高的,如果不使用有效的方法加以管理,很可能会陷入复杂的泥潭中不可自拔。而将程序分解成模块,则会将整体功能的复杂度有效的下分到各个模块中。每个模块只要能够管理好自己的复杂度就可以了。
提高代码的重用性
还是以造汽车为例,假设我造了一个很牛的发动机,多款车型都可以使用它。程序设计也一样,如果一个模块能够完成特定的功能,且与父程序耦合度较小,多个程序都可以使用它。
易于维护和扩展
小 A 写了一个程序,并将各个部分划分的非常明确,再加以人性化的函数命名和注释。即使有一天小 A 离职了,小 B 要接过来维护以及在此基础上再开发新的功能也不难。
既然模块化就这么多好处, 强大的 python 当然也会吸收这个优秀的设计思想,并且在语言中有所体现,那就是 python 的模块(module)。
什么是 python 中的模块?
先来看一个示例:
创建 python 文件 a.py,并在文件中定义函数
sumdef 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 对正则表达式的使用(上篇)
目录 引言 什么是正则表达式? 正则表达式有什么用? 正则表达式的语法及使用实例 正则表达式语法有哪些? 这些正则到底该怎么用? 小结 参考文档 系列文章列表 引言 刚接触正则表达式,我也曾被它们天书 ...
随机推荐
- CF特征码遍历
HOOK_游戏代码 8B 00 8B 08 8B 91 A8 00 00 00 地址-15 4E5E954E5EA 44E5E95DIRECT 从733E00开始搜 6B 00 94 51 6C 地址 ...
- sublime-text-how-to-jump-to-file-from-find-results-using-keyboard
http://209.116.186.231/#newwindow=1&q=sublime+text+find+results+jump http://stackoverflow.com/qu ...
- FunDA(4)- 数据流内容控制:Stream data element control
上节我们探讨了通过scalaz-stream-fs2来驱动一套数据处理流程,用fs2的Pipe类型来实现对数据流的逐行操作.本篇讨论准备在上节讨论的基础上对数据流的流动和元素操作进行优化完善.如数据流 ...
- 对Spring 及SpringMVC的理解
spring是一个轻型容器(light-weight Container),其核心是Bean工厂(Bean Factory),用以构造我们所需要的M(Model).在此基础之上,Spring提供了AO ...
- JS: 数据结构与算法之栈
栈 先来看一道题 Leetcode 32 Longest Valid Parentheses (最长有效括号) 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度. 示例 ...
- js04
接着看一些js的基础,这里主要说一下js的对象. 1.对象: js中的所有事物都可以看作是对象:字符串.数值.数组.函数... 内建对象:String Date Array ...
- android开发分辨率适配总结
重要概念 什么是屏幕尺寸.屏幕分辨率.屏幕像素密度? 什么是dp.dip.dpi.sp.px?他们之间的关系是什么? 什么是mdpi.hdpi.xdpi.xxdpi?如何计算和区分? 在下面的内容中我 ...
- Java之集合(二)ArrayDeque
转载请注明源出处:http://www.cnblogs.com/lighten/p/7283928.html 1.前言 上章讲解了Java中的集合接口和相关实现抽象类,本章开始介绍一些具体的实现类,第 ...
- 使用openssh-clients的scp命令来传输文件
了解openssh-client是请参阅:https://blog.csdn.net/u010215256/article/details/53239905 了解scp命令来传输文件请参阅:https ...
- 2018春招-今日头条笔试题5题(后附大佬答案-c++版)
1题目描述 在n个元素的数组中,找到差值为k的除重后的数字对 输入描述 第一行:n和k,n表示数字的个数,k表示差值 第二行:n个整数 输入样例 输入: 5 2 1 5 3 4 2 输出: 3 说明: ...