Python 简明教程 --- 17,Python 模块与包
微信公众号:码农充电站pro
个人主页:https://codeshellme.github.io
正确的判断来源于经验,然而经验来源于错误的判断。
—— Fred Brooks
目录
我们已经知道函数
是一种重复利用代码的机制。
本节我们来介绍模块
,Python 中的模块,也是一种重复利用代码的机制。我们可以将有特定功能
的代码(比如函数
,类
等)写在模块中,供他人使用,便于重复利用,便于维护。
在前面的章节中,我们也接触过模块。Python 功能强大的一个重要的原因,就是它有各种方便使用的模块。
Python 中的模块可以分为两种:
- 内建模块:Python 自带的模块,安装好Python 解释器后,内建模块就已经安装好了,直接
import
就可使用 - 第三方模块:由个人,公司或组织开发的模块,使用之前需要先安装,再
import
1,定义模块
我们写的Python 代码都是以.py
为后缀,Python 模块也是以.py
为后缀,其实每个Python 代码文件,都可以看做是一个Python 模块。
我们编写如下代码:
#! /usr/bin/env python3
# 定义一个字符串
PY = 'Hi Python.'
# 定义一个函数
def hello_py():
print('Hello Python.')
将该代码写在一个名为hello.py
的文件中,该文件就是一个hello
模块,将该文件放在~/hi
目录中。
注意:
在定义模块时,要避免与已有的模块起相同的名字,会引起冲突
2,使用模块
我们进入~/hi
目录,打开python3
交互式环境。
dir()
函数
在引入模块之前,我们先使用dir()
函数,来查看当前环境可用的函数/变量
:
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__']
可以看到,除了几个魔法方法
外,没有任何其他可用的函数/变量
。
我们定义一个变量s
:
>>> s = 'abc'
再用dir()
函数查看,可以看到多了一个s
变量:
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__', 's']
import
模块
在使用模块时,需要先引入模块。引入模块,需要使用import
关键字。
我们输入如下代码将hello
模块引入:
>>> import hello
再用dir()
函数,来查看当前环境可用的函数/变量
:
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'hello', 's']
可以看到,多了一个hello
。这是因为引入的模块会使用模块名
来作为可用的标识符。
我们可以用dir(hello)
来查看hello
模块支持的函数/变量
:
>>> dir(hello)
['__builtins__', '__cached__', '__doc__',
'__file__', '__loader__', '__name__',
'__package__', '__spec__', 'PY', 'hello_py']
可以看到,有两个标识符PY
和 hello_py
可以使用,但并不能看出PY
和hello_py
是一个函数,还是一个变量。
可以通过type(hello.标识符)
来查看标识符的类型:
>>> type(hello.PY) # 字符串
<class 'str'>
>>> type(hello.hello_py) # 函数
<class 'function'>
可见PY
是一个字符串, hello_py
是一个函数。
注意:
引用一个模块中的
标识符
,使用的是点.
操作符比如这里的
hello.PY
和hello.hello_py
我们可以这样来调用一个模块中的函数/变量
:
>>> hello.PY
'Hi Python.'
>>> hello.hello_py()
hello python.
3,模块文档注释
我们知道编写函数时可以有文档注释,同样模块中也可以有文档注释
,任何模块的第一个字符串将作为该模块的文档注释,如下:
#! /usr/bin/env python3
'''这是hello 模块的说明文档'''
# 定义一个字符串
PY = 'Hi Python.'
# 定义一个函数
def hello_py():
print('Hello Python.')
可以用魔法方法__doc__
来访问文档字符串:
>>> hello.__doc__
'这是hello 模块的说明文档'
4,私有变量和私有函数
在Python 模块中,规定以单下划线_
和双下划线__
开头的函数/变量
,被称为私有函数/变量
(private),不应该被外部模块使用。
不以_
和__
开头的函数/变量
,被称为共有函数/变量
(public),可被外部使用。
如下:
#! /usr/bin/env python3
'''这是hello 模块的说明文档'''
PY = 'Hi Python.'
def _hello(s):
print('hello %s.' % s)
def hello_py():
_hello('python')
def hello_java():
_hello('java')
在该模块中,hello_py
,hello_java
和 PY
是提供给外部使用的,而_hello
只建议在模块内部使用。
其实在Python 中的这种访问权限
的控制,实际上是“假的”,因为从技术上来说,这种方式,并不能完全的禁止外部调用,只是相当于一种编程规范
。
如果我们非要使用这种以下划线
开头的函数,从技术上来说也是行得通的,如下:
>>> import hello
>>> hello._hello('python')
hello python.
但一般我们并不建议这样做。
5,__name__
属性
每个模块中都自带一个__name__
属性,运行当前模块时,该属性的值为__main__
,这经常与if 语句
一起,被用来测试代码
使用,这类似其它编程语言中的main()
函数。
如下,文件名为hello.py
:
#! /usr/bin/env python3
'''这是hello 模块的说明文档'''
PY = 'Hi Python.'
def hello_py():
print('hello python.')
if __name__ == '__main__':
hello_py()
当我们在命令行执行python3 hello.py
时,if __name__ == '__main__':
内的代码块都会被执行。
但当在其它模块引入hello
模块时,if __name__ == '__main__':
内的代码块并不会被执行。
6,Python 包
Python 包是比模块更高层的概念,一个Python 包中可以包含一个或多个模块。
一个包含多个模块的Python 包结构如下:
my_abc/
├── __init__.py
├── a.py
├── b.py
└── c/
├── __init__.py
└── d.py
my_abc
是顶层包名,my_abc
目录中有一个__init__.py
文件,拥有这个__init__.py
文件的目录才会被认为是一个Python 包,否则,只是一个普通的目录。
__init__.py
文件可以为空,也可以有Python 代码,my_abc/c/
目录中的__init__.py
文件也是同样的道理。
向my_abc/__init__.py
文件中写入如下内容:
#! /usr/bin/env python3
print('这是my_abc 模块中的 __init__ 文件')
向my_abc/c/__init__.py
文件中写入如下内容:
#! /usr/bin/env python3
print('这是my_abc.c 模块中的 __init__ 文件')
我们在my_abc
同级目录下运行python3
解释器,当我们引入my_abc
包时,my_abc/__init__.py
文件中的代码就会被执行:
>>> import my_abc # 第一次引入
这是my_abc 模块中的 __init__ 文件
>>> import my_abc # 第二次引入
可以看到当输入import my_abc
代码时,字符串这是my_abc 模块中的 __init__ 文件
被输出,说明my_abc/__init__.py
文件中的内容被执行了。
紧接着再次输入import my_abc
代码时,并没有任何输出,说明某个模块中的__init__.py
文件中的内容,只有在第一次导入该模块时,才会执行。
此时,当前环境中多了一个可用的my_abc
标识符:
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'my_abc']
同样,当我们引入my_abc.c
包时,my_abc/c/__init__.py
文件中的代码就会被执行:
>>> import my_abc.c
这是my_abc.c 模块中的 __init__ 文件
from ... import ...
语法
我们可以使用from... import...
语法来引入一个包中的模块,比如我们引入my_abc
包中的a
模块,如下:
>>> from my_abc import a
这是my_abc 模块中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a']
可以看到,使用该语法引入a
模块时,my_abc/__init__.py
文件中的内容也执行了,此时,当前环境中多了一个可用的a
标识符。
from ... import ... as...
语法
可以使用as
关键字为模块重命名,例如将a
模块重命名为a_test
:
>>> from my_abc import a as a_test
这是my_abc 模块中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a_test']
可以看到本次导入时,没有a
标识符,取而代之的a_test
。
from ... import *
语法
from ... import *
语法会导入某个包中__init__.py
文件内的__all__
列表的所有内容,当__all__
列表不存在时,该语法将导入__init__.py
文件中的所有可用标识符。
例如,my_abc/__init__.py
文件中内容如下,没有__all__
列表:
#! /usr/bin/env python3
print('这是my_abc 模块中的 __init__ 文件')
i = 1
j = 2
def hello():
print('hello')
使用from ... import *
语法,将导入__init__.py
文件中所有可用的标识符:
>>> from my_abc import *
这是my_abc 模块中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__',
'hello', 'i', 'j']
可见__init__.py
文件中的所有可用标识符,都被导入到了当前环境中。
如果在__init__.py
文件中声明一个__all__
列表,如下:
#! /usr/bin/env python3
print('这是my_abc 模块中的 __init__ 文件')
__all__ = ['i', 'hello']
i = 1
j = 2
def hello():
print('hello')
from ... import *
语法将只会导入__all__
列表中的内容,如下
>>> from my_abc import *
这是my_abc 模块中的 __init__ 文件
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__',
'hello', 'i']
因为__all__
列表中只有i
, hello
两个元素,所以只导入了这两个标识符。
7,模块的导入路径
从内建模块sys
中的path
标识符可查看导入模块的路径:
>>> import sys
>>> sys.path
['',
'/usr/lib/python36.zip',
'/usr/lib/python3.6',
'/usr/lib/python3.6/lib-dynload',
'/usr/local/lib/python3.6/dist-packages',
'/usr/lib/python3/dist-packages']
其中,第一个''
表示从当前目录
中导入,其它的目录是系统中的标准包路径
。
Python 会按照该列表,从前往后依次从这些路径中导入你想import
的模块,先从当前目录中尝试导入你想导入的模块,如果没有找到,会依次从后边的路径去查找,如果都没有找到,最后会抛出异常。
比如,现在有一个test
目录,该目录下有一个a.py
文件:
test/
└── a.py
如果在test
同级目录下导入a.py
,将出现异常,因为无法从sys.path
中找到a.py
:
>>> import a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'a'
此时,可以将./test
目录append
到sys.path
中,然后再import
:
>>> import sys
>>> sys.path.append('./test')
>>> import a # 没有出现异常,说明导入成功
>>> dir()
['__annotations__', '__builtins__',
'__doc__', '__loader__', '__name__',
'__package__', '__spec__',
'a', 'sys'] # a 模块被成功导入
(完。)
推荐阅读:
Python 简明教程 --- 14,Python 数据结构进阶
Python 简明教程 --- 16,Python 高阶函数
欢迎关注作者公众号,获取更多技术干货。
Python 简明教程 --- 17,Python 模块与包的更多相关文章
- python基础教程总结9——模块,包,标准库
1. 模块 在python中一个文件可以被看成一个独立模块,而包对应着文件夹,模块把python代码分成一些有组织的代码段,通过导入的方式实现代码重用. 1.1 模块搜索路径 导入模块时,是按照sys ...
- Python 简明教程 --- 18,Python 面向对象
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 代码能借用就借用. -- Tom Duff 目录 编程可分为面向过程编程和面向对象编程,它们是两种不 ...
- Python 简明教程 --- 19,Python 类与对象
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 那些能用计算机迅速解决的问题,就别用手做了. -- Tom Duff 目录 上一节 我们介绍了Pyt ...
- Python 简明教程 --- 20,Python 类中的属性与方法
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 与客户保持良好的关系可以使生产率加倍. -- Larry Bernstain 目录 类中的变量称为属 ...
- Python 简明教程 --- 21,Python 继承与多态
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 程序不是年轻的专利,但是,它属于年轻. 目录 我们已经知道封装,继承和多态 是面向对象的三大特征,面 ...
- Python 简明教程 --- 22,Python 闭包与装饰器
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 当你选择了一种语言,意味着你还选择了一组技术.一个社区. 目录 本节我们来介绍闭包与装饰器. 闭包与 ...
- python简明教程
Python简明教程 MachinePlay关注 0.7072018.09.26 01:49:43字数 2,805阅读 9,287 Python一小时快速入门 1.Python简介 pylogo. ...
- Python 简明教程 --- 26,Python 多进程编程
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 学编程最有效的方法是动手敲代码. 目录 1,什么是多进程 我们所写的Python 代码就是一个程序, ...
- 《Python简明教程》总结
Python经典教程<Python简明教程> 目录: 为什么Python 安装Python 体验Python Python数据类型 运算符与表达式 控制流 函数 模块 数据结构 解决问题 ...
随机推荐
- ie时间格式NAN-NAN-NAN
js的日期对象可以识别的日期字符串有四种: 1.YYYY-MM-DD 2019-11-11 01:01:012.MM-DD-YYYY 11-11-2019 01:01:013.YYYY/MM/DD 2 ...
- ArcCore重构-打通Can各层ID配置
https://mp.weixin.qq.com/s/JX7VZwyMqk_9iVMm_N2pxA https://mp.weixin.qq.com/s/5Y8Dt9j1-NQmnjfYhE19dg ...
- 百度云百度网盘VIP不限速破解版绿色版-实测可用
百度云百度网盘不限速VIP破解版绿色版-下载地址:https://www.90pan.com/b1548999
- 高性能可扩展mysql 笔记(三)Hash分区、RANGE分区、LIST分区
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.MySQL分区表操作 1.定义:数据库表分区是数据库基本设计规范之一,分区表在物理上表现为多个文件, ...
- Java实现 LeetCode 740 删除与获得点数(递推 || 动态规划?打家劫舍Ⅳ)
740. 删除与获得点数 给定一个整数数组 nums ,你可以对它进行一些操作. 每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数.之后,你必须删除每个等于 nums[ ...
- Java实现 LeetCode 589 N叉树的前序遍历(遍历树)
589. N叉树的前序遍历 给定一个 N 叉树,返回其节点值的前序遍历. 例如,给定一个 3叉树 : 返回其前序遍历: [1,3,5,6,2,4]. 说明: 递归法很简单,你可以使用迭代法完成此题吗? ...
- Java实现 LeetCode 546 移除盒子(递归,vivo秋招)
546. 移除盒子 给出一些不同颜色的盒子,盒子的颜色由数字表示,即不同的数字表示不同的颜色. 你将经过若干轮操作去去掉盒子,直到所有的盒子都去掉为止.每一轮你可以移除具有相同颜色的连续 k 个盒子( ...
- Java实现 洛谷 P1421 小玉买文具
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = ...
- Java实现二分图的最大权匹配
1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 解决这个问题可以用KM算法.理解KM算法需要首先理解& ...
- java实现第五届蓝桥杯李白打酒
李白打酒 题目描述 话说大诗人李白,一生好饮.幸好他从不开车. 一天,他提着酒壶,从家里出来,酒壶中有酒2斗.他边走边唱: 无事街上走,提壶去打酒. 逢店加一倍,遇花喝一斗. 这一路上,他一共遇到店5 ...