微信公众号:码农充电站pro

个人主页:https://codeshellme.github.io

正确的判断来源于经验,然而经验来源于错误的判断。

—— Fred Brooks

目录

我们已经知道函数是一种重复利用代码的机制。

本节我们来介绍模块,Python 中的模块,也是一种重复利用代码的机制。我们可以将有特定功能的代码(比如函数等)写在模块中,供他人使用,便于重复利用,便于维护。

在前面的章节中,我们也接触过模块。Python 功能强大的一个重要的原因,就是它有各种方便使用的模块。

Python 中的模块可以分为两种:

  • 内建模块:Python 自带的模块,安装好Python 解释器后,内建模块就已经安装好了,直接import 就可使用
  • 第三方模块:由个人,公司或组织开发的模块,使用之前需要先安装,再import

1,定义模块

我们写的Python 代码都是以.py 为后缀,Python 模块也是以.py 为后缀,其实每个Python 代码文件,都可以看做是一个Python 模块。

我们编写如下代码:

  1. #! /usr/bin/env python3
  2. # 定义一个字符串
  3. PY = 'Hi Python.'
  4. # 定义一个函数
  5. def hello_py():
  6. print('Hello Python.')

将该代码写在一个名为hello.py 的文件中,该文件就是一个hello 模块,将该文件放在~/hi 目录中。

注意:

在定义模块时,要避免与已有的模块起相同的名字,会引起冲突

2,使用模块

我们进入~/hi 目录,打开python3 交互式环境。

dir() 函数

在引入模块之前,我们先使用dir() 函数,来查看当前环境可用的函数/变量

  1. >>> dir()
  2. ['__annotations__', '__builtins__',
  3. '__doc__', '__loader__', '__name__',
  4. '__package__', '__spec__']

可以看到,除了几个魔法方法外,没有任何其他可用的函数/变量

我们定义一个变量s

  1. >>> s = 'abc'

再用dir() 函数查看,可以看到多了一个s 变量:

  1. >>> dir()
  2. ['__annotations__', '__builtins__',
  3. '__doc__', '__loader__', '__name__',
  4. '__package__', '__spec__', 's']

import 模块

在使用模块时,需要先引入模块。引入模块,需要使用import 关键字。

我们输入如下代码将hello 模块引入:

  1. >>> import hello

再用dir() 函数,来查看当前环境可用的函数/变量

  1. >>> dir()
  2. ['__annotations__', '__builtins__',
  3. '__doc__', '__loader__', '__name__',
  4. '__package__', '__spec__', 'hello', 's']

可以看到,多了一个hello。这是因为引入的模块会使用模块名来作为可用的标识符。

我们可以用dir(hello) 来查看hello 模块支持的函数/变量

  1. >>> dir(hello)
  2. ['__builtins__', '__cached__', '__doc__',
  3. '__file__', '__loader__', '__name__',
  4. '__package__', '__spec__', 'PY', 'hello_py']

可以看到,有两个标识符PYhello_py 可以使用,但并不能看出PYhello_py 是一个函数,还是一个变量。

可以通过type(hello.标识符) 来查看标识符的类型:

  1. >>> type(hello.PY) # 字符串
  2. <class 'str'>
  3. >>> type(hello.hello_py) # 函数
  4. <class 'function'>

可见PY 是一个字符串, hello_py 是一个函数。

注意:

引用一个模块中的标识符,使用的是点. 操作符

比如这里的hello.PYhello.hello_py

我们可以这样来调用一个模块中的函数/变量

  1. >>> hello.PY
  2. 'Hi Python.'
  3. >>> hello.hello_py()
  4. hello python.

3,模块文档注释

我们知道编写函数时可以有文档注释,同样模块中也可以有文档注释,任何模块的第一个字符串将作为该模块的文档注释,如下:

  1. #! /usr/bin/env python3
  2. '''这是hello 模块的说明文档'''
  3. # 定义一个字符串
  4. PY = 'Hi Python.'
  5. # 定义一个函数
  6. def hello_py():
  7. print('Hello Python.')

可以用魔法方法__doc__ 来访问文档字符串:

  1. >>> hello.__doc__
  2. '这是hello 模块的说明文档'

4,私有变量和私有函数

在Python 模块中,规定以单下划线_ 和双下划线__ 开头的函数/变量,被称为私有函数/变量(private),不应该被外部模块使用。

不以___ 开头的函数/变量,被称为共有函数/变量 (public),可被外部使用。

如下:

  1. #! /usr/bin/env python3
  2. '''这是hello 模块的说明文档'''
  3. PY = 'Hi Python.'
  4. def _hello(s):
  5. print('hello %s.' % s)
  6. def hello_py():
  7. _hello('python')
  8. def hello_java():
  9. _hello('java')

在该模块中,hello_pyhello_javaPY 是提供给外部使用的,而_hello 只建议在模块内部使用。

其实在Python 中的这种访问权限的控制,实际上是“假的”,因为从技术上来说,这种方式,并不能完全的禁止外部调用,只是相当于一种编程规范

如果我们非要使用这种以下划线开头的函数,从技术上来说也是行得通的,如下:

  1. >>> import hello
  2. >>> hello._hello('python')
  3. hello python.

但一般我们并不建议这样做。

5,__name__ 属性

每个模块中都自带一个__name__ 属性,运行当前模块时,该属性的值为__main__,这经常与if 语句一起,被用来测试代码使用,这类似其它编程语言中的main() 函数。

如下,文件名为hello.py

  1. #! /usr/bin/env python3
  2. '''这是hello 模块的说明文档'''
  3. PY = 'Hi Python.'
  4. def hello_py():
  5. print('hello python.')
  6. if __name__ == '__main__':
  7. hello_py()

当我们在命令行执行python3 hello.py 时,if __name__ == '__main__': 内的代码块都会被执行。

但当在其它模块引入hello 模块时,if __name__ == '__main__': 内的代码块并不会被执行。

6,Python 包

Python 包是比模块更高层的概念,一个Python 包中可以包含一个或多个模块。

一个包含多个模块的Python 包结构如下:

  1. my_abc/
  2. ├── __init__.py
  3. ├── a.py
  4. ├── b.py
  5. └── c/
  6. ├── __init__.py
  7. └── d.py

my_abc 是顶层包名,my_abc 目录中有一个__init__.py 文件,拥有这个__init__.py 文件的目录才会被认为是一个Python 包,否则,只是一个普通的目录。

__init__.py 文件可以为空,也可以有Python 代码,my_abc/c/ 目录中的__init__.py 文件也是同样的道理。

my_abc/__init__.py 文件中写入如下内容:

  1. #! /usr/bin/env python3
  2. print('这是my_abc 模块中的 __init__ 文件')

my_abc/c/__init__.py 文件中写入如下内容:

  1. #! /usr/bin/env python3
  2. print('这是my_abc.c 模块中的 __init__ 文件')

我们在my_abc 同级目录下运行python3 解释器,当我们引入my_abc 包时,my_abc/__init__.py 文件中的代码就会被执行:

  1. >>> import my_abc # 第一次引入
  2. 这是my_abc 模块中的 __init__ 文件
  3. >>> import my_abc # 第二次引入

可以看到当输入import my_abc 代码时,字符串这是my_abc 模块中的 __init__ 文件 被输出,说明my_abc/__init__.py 文件中的内容被执行了。

紧接着再次输入import my_abc 代码时,并没有任何输出,说明某个模块中的__init__.py 文件中的内容,只有在第一次导入该模块时,才会执行。

此时,当前环境中多了一个可用的my_abc 标识符:

  1. >>> dir()
  2. ['__annotations__', '__builtins__',
  3. '__doc__', '__loader__', '__name__',
  4. '__package__', '__spec__', 'my_abc']

同样,当我们引入my_abc.c 包时,my_abc/c/__init__.py 文件中的代码就会被执行:

  1. >>> import my_abc.c
  2. 这是my_abc.c 模块中的 __init__ 文件

from ... import ... 语法

我们可以使用from... import... 语法来引入一个包中的模块,比如我们引入my_abc 包中的a 模块,如下:

  1. >>> from my_abc import a
  2. 这是my_abc 模块中的 __init__ 文件
  3. >>> dir()
  4. ['__annotations__', '__builtins__',
  5. '__doc__', '__loader__', '__name__',
  6. '__package__', '__spec__', 'a']

可以看到,使用该语法引入a 模块时,my_abc/__init__.py 文件中的内容也执行了,此时,当前环境中多了一个可用的a 标识符。

from ... import ... as... 语法

可以使用as 关键字为模块重命名,例如将a 模块重命名为a_test

  1. >>> from my_abc import a as a_test
  2. 这是my_abc 模块中的 __init__ 文件
  3. >>> dir()
  4. ['__annotations__', '__builtins__',
  5. '__doc__', '__loader__', '__name__',
  6. '__package__', '__spec__', 'a_test']

可以看到本次导入时,没有a 标识符,取而代之的a_test

from ... import * 语法

from ... import * 语法会导入某个包中__init__.py 文件内的__all__ 列表的所有内容,当__all__ 列表不存在时,该语法将导入__init__.py 文件中的所有可用标识符。

例如,my_abc/__init__.py 文件中内容如下,没有__all__ 列表:

  1. #! /usr/bin/env python3
  2. print('这是my_abc 模块中的 __init__ 文件')
  3. i = 1
  4. j = 2
  5. def hello():
  6. print('hello')

使用from ... import * 语法,将导入__init__.py 文件中所有可用的标识符:

  1. >>> from my_abc import *
  2. 这是my_abc 模块中的 __init__ 文件
  3. >>> dir()
  4. ['__annotations__', '__builtins__',
  5. '__doc__', '__loader__', '__name__',
  6. '__package__', '__spec__',
  7. 'hello', 'i', 'j']

可见__init__.py 文件中的所有可用标识符,都被导入到了当前环境中。

如果在__init__.py 文件中声明一个__all__ 列表,如下:

  1. #! /usr/bin/env python3
  2. print('这是my_abc 模块中的 __init__ 文件')
  3. __all__ = ['i', 'hello']
  4. i = 1
  5. j = 2
  6. def hello():
  7. print('hello')

from ... import * 语法将只会导入__all__ 列表中的内容,如下

  1. >>> from my_abc import *
  2. 这是my_abc 模块中的 __init__ 文件
  3. >>> dir()
  4. ['__annotations__', '__builtins__',
  5. '__doc__', '__loader__', '__name__',
  6. '__package__', '__spec__',
  7. 'hello', 'i']

因为__all__ 列表中只有i, hello 两个元素,所以只导入了这两个标识符。

7,模块的导入路径

从内建模块sys 中的path 标识符可查看导入模块的路径:

  1. >>> import sys
  2. >>> sys.path
  3. ['',
  4. '/usr/lib/python36.zip',
  5. '/usr/lib/python3.6',
  6. '/usr/lib/python3.6/lib-dynload',
  7. '/usr/local/lib/python3.6/dist-packages',
  8. '/usr/lib/python3/dist-packages']

其中,第一个'' 表示从当前目录中导入,其它的目录是系统中的标准包路径

Python 会按照该列表,从前往后依次从这些路径中导入你想import 的模块,先从当前目录中尝试导入你想导入的模块,如果没有找到,会依次从后边的路径去查找,如果都没有找到,最后会抛出异常。

比如,现在有一个test 目录,该目录下有一个a.py 文件:

  1. test/
  2. └── a.py

如果在test 同级目录下导入a.py,将出现异常,因为无法从sys.path 中找到a.py

  1. >>> import a
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4. ModuleNotFoundError: No module named 'a'

此时,可以将./test 目录appendsys.path 中,然后再import

  1. >>> import sys
  2. >>> sys.path.append('./test')
  3. >>> import a # 没有出现异常,说明导入成功
  4. >>> dir()
  5. ['__annotations__', '__builtins__',
  6. '__doc__', '__loader__', '__name__',
  7. '__package__', '__spec__',
  8. 'a', 'sys'] # a 模块被成功导入

(完。)


推荐阅读:

Python 简明教程 --- 12,Python 字典

Python 简明教程 --- 13,Python 集合

Python 简明教程 --- 14,Python 数据结构进阶

Python 简明教程 --- 15,Python 函数

Python 简明教程 --- 16,Python 高阶函数


欢迎关注作者公众号,获取更多技术干货。

Python 简明教程 --- 17,Python 模块与包的更多相关文章

  1. python基础教程总结9——模块,包,标准库

    1. 模块 在python中一个文件可以被看成一个独立模块,而包对应着文件夹,模块把python代码分成一些有组织的代码段,通过导入的方式实现代码重用. 1.1 模块搜索路径 导入模块时,是按照sys ...

  2. Python 简明教程 --- 18,Python 面向对象

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 代码能借用就借用. -- Tom Duff 目录 编程可分为面向过程编程和面向对象编程,它们是两种不 ...

  3. Python 简明教程 --- 19,Python 类与对象

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 那些能用计算机迅速解决的问题,就别用手做了. -- Tom Duff 目录 上一节 我们介绍了Pyt ...

  4. Python 简明教程 --- 20,Python 类中的属性与方法

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 与客户保持良好的关系可以使生产率加倍. -- Larry Bernstain 目录 类中的变量称为属 ...

  5. Python 简明教程 --- 21,Python 继承与多态

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 程序不是年轻的专利,但是,它属于年轻. 目录 我们已经知道封装,继承和多态 是面向对象的三大特征,面 ...

  6. Python 简明教程 --- 22,Python 闭包与装饰器

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 当你选择了一种语言,意味着你还选择了一组技术.一个社区. 目录 本节我们来介绍闭包与装饰器. 闭包与 ...

  7. python简明教程

    Python简明教程 MachinePlay关注 0.7072018.09.26 01:49:43字数 2,805阅读 9,287 Python一小时快速入门 1.Python简介   pylogo. ...

  8. Python 简明教程 --- 26,Python 多进程编程

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 学编程最有效的方法是动手敲代码. 目录 1,什么是多进程 我们所写的Python 代码就是一个程序, ...

  9. 《Python简明教程》总结

    Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...

随机推荐

  1. 50个SQL语句(MySQL版) 问题十四

    --------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...

  2. 爱奇艺|B站|优酷|腾讯视频高清无水印视频下载方法(软件工具教程)

    导读:经常在大型视频网站平台上看到一些很价值和视频,希望能高清无水印下载到本地学习观看,今天小程序定制开发代码哥DaiMaGe6给大家分享一招免费下载全网高清无水印视频的方法. 高清无水印视频下载工具 ...

  3. Eclipse中常用快捷键的使用

    1.补全代码的声明:alt + / 2.快速修复: ctrl + 1 3.批量导包:ctrl + shift + o 4.使用单行注释:ctrl + / 5.使用多行注释: ctrl + shift ...

  4. JAVA实现对称加密

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.对称加密算法DES 1.概述:采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这 ...

  5. Java实现 LeetCode 283 移动零

    283. 移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必 ...

  6. Java实现 LeetCode 274 H指数

    274. H指数 给定一位研究者论文被引用次数的数组(被引用次数是非负整数).编写一个方法,计算出研究者的 h 指数. h 指数的定义: "h 代表"高引用次数"(hig ...

  7. IDEA连接远程服务器Docker部署Spring Boot项目

    开始前的准备工作 拥有一台云服务器,我的是腾讯云服务器(CentOS7) 腾讯云服务器安装Docker,我的版本是Docker 19.03.9,关于安装和配置镜像加速器可以查阅我的另一篇博文:http ...

  8. javafx分别设置四个边框

    package border; import javafx.application.Application; import javafx.geometry.Insets; import javafx. ...

  9. Spring AOP 之二:Pointcut注解表达式

    简介 在Spring AOP概述中我们重点注意的是AOP的整体流程和Advice,简化了一些其他的东西,其中就有一些对灵活应用Spring AOP很重要的知识点,例如Pointcut表达式,下面就介绍 ...

  10. Node.js 学习笔记(二)

    express是nodejs的一个轻量级框架. express的功能很简单, 功能依赖中间件. 安装:在你的项目文件价里打开cmd窗口,在里面使用npm工具(就是在cmd里输入 npm install ...