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 对正则表达式的使用(上篇)
目录 引言 什么是正则表达式? 正则表达式有什么用? 正则表达式的语法及使用实例 正则表达式语法有哪些? 这些正则到底该怎么用? 小结 参考文档 系列文章列表 引言 刚接触正则表达式,我也曾被它们天书 ...
随机推荐
- Day 9 函数的初识1
def my_len(): l1 = [1,2,3,5,6] print(111) print(222) return print(333)print(my_len()) 一.函数的定义1.遇到ret ...
- HDU4622 Reincarnation
题目链接:戳我 因为对应的很多询问,所以我们一定要将每一种询问先处理出来,然后O(1)查询. 至于怎么处理出来子串的子串呢? 我们固定左端点,然后依次加入子串即可.然后统计的时候直接统计last那一个 ...
- ThreadLocal模式与synchronized关键字的比较
ThreadLocal模式与synchronized关键字都是用于处理多线程并发访问变量的问题.只是两者处理问题的角度和思路不同. 1)ThreadLocal是一个Java类,通过对当前线程(Thre ...
- c++之sleep函数
c++之sleep函数 c++中使用sleep函数需要导入第三方库,标准库中没有该函数实现. 我们导入window.h使用Sleep()方法,注意:第一个S要大写,括号中的表示的整数倍的毫秒 Slee ...
- POJ 1102
#include<iostream>// cheng da cai zi 11.14 using namespace std; int main() { int i; int j; int ...
- Label Propagation Algorithm LPA 标签传播算法解析及matlab代码实现
转载请注明出处:http://www.cnblogs.com/bethansy/p/6953625.html LPA算法的思路: 首先每个节点有一个自己特有的标签,节点会选择自己邻居中出现次数最多的标 ...
- Day1 Excel基本知识
1. 数据分析过程 2. 2016增强版Excel 特性 科学管理,科学决策的工具 精细化,量化分析的工具 3. Excel 学什么? 计算机解决问题的思维(抽象) 数据分析的思路与方法 掌握技巧和方 ...
- tensorflow训练了10万次,运行完毕,对这个word2vec终于有点感觉了
tensorflow训练了10万次,运行完毕,对这个word2vec终于有点感觉了 感觉它能找到词与词之间的关系,应该可以用来做推荐系统.自动摘要.相关搜索.联想什么的 tensorflow1.1.0 ...
- EF基础知识小记四(数据库=>模型设计器)
EF基础知识小记三(设计器=>数据库)介绍了如何创建一个空设计器模型,并如何将模型同步到数据库的表中,本文则主要介绍如何将一个存在的数据库同步到模型设计器中.为了能快速的模拟这个过程,给出一下建 ...
- Form表单如何可以传递多个值传递List数组对象到后台的解决办法
举例说明: 后台有一个对象 User ,结构如下: 后台有一个对象 User ,结构如下: public class User{ private String username; private Li ...