第十章 开箱即用
本章简要介绍模块的工作原理以及如何探索模块以获悉其提供的功能,然后概述标准库,重点是几个很有用的模块。
10.1 模块
使用import将函数从外部模块导入到程序中。
'''
import math as ma
Pi=3.1415926
v=ma.sin(Pi/6)
print(v)
'''
0.49999999226497965
------------------
(program exited with code: 0)
请按任意键继续. . .
10.1.1 模块就是程序
创建模块很简单,只要新建一个文件把它定义为xx.py保存。在另一个文件中加入import xx(明示文件地址)即可调用这个文件中的内容,这个xx.py就是模块。文件的存储位置也很重要,地址不对也不能调用此模块。
另外还可以把作为模块文件的地址设置到系统path的参数,这样在调用时就不需要明示模块地址了。
在一个文件中调用模块,只需调用以此。
10.1.2 模块是用来下定义的
在模块中定义的类和函数以及对其进行赋值的变量都将成为模块的属性。
1. 在模块中定义函数
在程序中访问模块中的函数格式为:模块名.函数名
在模块的全局作用域内定义的名称都可像上面这样访问。
2. 在模块中添加测试代码
模块是为了重用代码,其中只能定义类、函数和变量及常量,不能把实例化的类和调用函数作为模块的引入内容。
在主程序中(包括解释器的交互式提示符),变量__name__的值是'__main__',而在导入的模块中,这个变量被设置为该模块的名称。因此,要让模块中测试代码的行为更合理,可将其放在一条if语句中,
如把xx.py文件作为模块:
'''
#xx.py
def hello():
pass
def test():
hello()
if __name__=='__main__':test()
'''
10.1.3 让模块可用
1. 将模块放在正确的位置
可在模块sys的变量path中找到目录列表(即搜索路径)。
'''
import sys,pprint #引入sys和pprint两个模块
pprint.pprint(sys.path)
'''
['E:\\pythonProjects',
'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip',
'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs',
'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\lib',
'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37',
'C:\\Users\\xx\\AppData\\Roaming\\Python\\Python37\\site-packages',
'C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages
']
------------------
(program exited with code: 0)
请按任意键继续. . .
注:如果要打印的数据结构太大,一行容纳不下,可使用模块pprint中的函数pprint(而不是普通print语句)。pprint是个卓越的打印函数,能够更妥善地打印输出。
只要模块位于类似于site-packages这样的地方并在文件名前加入another_前缀,所有的程序就都能够导入它。
2. 告诉解释器到哪里去查找
标准做法是设置系统变量PYTHONPATH,并把模块所在的目录包含在系统环境变量PYTHONPATH中。
10.1.4 包
为组织模块,可将其编组为包(package)。包其实就是另一种模块,但有趣的是它们可包含其他模块。模块存储在扩展名为.py的文件中,而包则是一个目录。在成为包的目录中必须包含__init__.py文件,无论此文件中有没有内容。
要将模块加入包中,只需将模块文件放在包目录中即可。在包中嵌套其他包。
假如文件的包结构如下:
~/python/ PYTHONPATH中的目录
~/python/drawing/ 包目录(包drawing)
~/python/drawing/__init__.py 包代码(模块drawing)
~/python/drawing/colors.py 模块colors
~/python/drawing/shapes.py 模块shapes
引入代码如下:
'''
'''
import drawing # (1) 导入drawing包
import drawing.colors # (2) 导入drawing包中的模块colors
from drawing import shapes # (3) 导入模块shapes
'''
'''
那么,执行第1条语句后,便可使用目录drawing中文件__init__.py的内容,但不能使用模块shapes和colors的内容。执行第2条语句后,便可使用模块colors,但只能通过全限定名drawing.colors来使用。执行第3条语句后,便可使用简化名(即shapes)来使用模块shapes。请注意,这些语句只是示例,并不用像这里做的那样,先导入包再导入其中的模块。换而言之,完全可以只使用第2条语句,第3条语句亦如此。
10.2 探索模块
如何探索模块。这是一种很有用的技能。
10.2.1 模块包含什么
要探索模块,最直接的方式是使用Python解释器进行研究。
i、将模块导入
ii、要查明模块包含哪些东西,可使用函数dir,它列出对象的所有属性(对于模块,它列出所有的函数、类、变量等)。dir(包名)。用[n for n in dir(包名) if not n.startswith('_')] 过滤下划线。
1、__all__属性
这个变量包含一个列表,它与前面使用列表推导创建的列表类似,但是在模块内部设置的。
下面以copy这个包为例来看看这个列表包含的内容:
'''
import copy
li_copy=dir(copy)
pprint.pprint(li_copy)
print('*'*80)
li=[n for n in dir(li_copy) if not n.startswith('_')]
print(li)
print('&'*80)
al=copy.__all__
print(al)
'''
['Error',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_copy_dispatch',
'_copy_immutable',
'_deepcopy_atomic',
'_deepcopy_dict',
'_deepcopy_dispatch',
'_deepcopy_list',
'_deepcopy_method',
'_deepcopy_tuple',
'_keep_alive',
'_reconstruct',
'copy',
'deepcopy',
'dispatch_table',
'error']
********************************************************************************
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove
', 'reverse', 'sort']
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
['Error', 'copy', 'deepcopy']
------------------
(program exited with code: 0)
请按任意键继续. . .
__all__的源文件是这样的(查看源文件可以到源文件存在的目录中查找。也可以根据使用的编辑器的功能查找。比如有的编译器是Ctrl+鼠标左键点击相应的变量或函数,也可以查看官网文档,地址:https://docs.python.org/3/)。
'''
__all__= ["Error", "copy", "deepcopy"]
'''
__all__的作用旨在定义模块的公有接口。它告诉解释器从这个模块导入所有的名称意味着什么。因此,如果你使用如下代码:
from copy import *
将只能得到变量__all__中列出的4个函数。要导入PyStringMap,必须显式地:导入copy并使用copy.PyStringMap;或者使用from copy import PyStringMap。
编写模块时,像这样设置__all__也很有用。因为模块可能包含大量其他程序不需要的变量、函数和类,比较周全的做法是将它们过滤掉。如果不设置__all__,则会在以import *方式导入时,导入所有不以下划线打头的全局名称。
10.2.2 使用 help 获取帮助
毕竟以上探索变量和函数的方法是有限的,它局限于对python语言掌握的程度。有一个标准函数可提供你通常需要的所有信息,它就是help
格式是help(包名.变量——或函数名)这种获取帮助的文档,实际上用包名.函数名.__doc__也可以得到一些信息,这就是模块文档。相比于直接查看文档字符串,使用help的优点是可获取更多的信息。
10.2.3 文档
文档是有关模块信息的自然来源。查看模块本身比其它获得有关信息的方法要快得多。例如,你可能想知道range的参数是什么?在这种情况下,与其在Python图书或标准Python文档中查找对range的描述,不如直接检查这个函数。
'''
print(range.__doc__)
'''
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
------------------
(program exited with code: 0)
请按任意键继续. . .
然而,并非每个模块和函数都有详尽的文档字符串(虽然应该如此),且有时需要有关工作原理的更详尽描述。从网上下载的大多数模块都有配套文档。
10.2.4 使用源代码
在大多数情况下,前面讨论的探索技巧都够用了。但要真正理解Python语言,可能需要了解一些不阅读源代码就无法了解的事情。
查看源代码所在的位置可以用__file__变量,例如:
'''
print(copy.__file__)
'''
C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\copy.py
------------------
(program exited with code: 0)
请按任意键继续. . .
10.3 标准库:一些深受欢迎的模块
10.3.1 sys
模块sys让你能够访问与Python解释器紧密相关的变量和函数如:
argv 传递给Python解释器的参数,命令行参数,包括脚本名
exit([arg]) 退出当前程序,可通过可选参数指定返回值或错误消息
modules 一个字典,将模块名映射到加载的模块
path 一个列表,包含要在其中查找模块的目录的名称
platform 一个平台标识符--运行解释器的“平台”名称,如sunos5或win32
stdin 标准输入流——一个类似于文件的对象
stdout 标准输出流——一个类似于文件的对象
stderr 标准错误流——一个类似于文件的对象
10.3.2 os
模块os功能是能够访问多个操作系统服务。它包含的内容很多,以下列出部分函数和变量。
environ 包含环境变量的映射
system(command) 在子shell中执行操作系统命令,用于运行外部程序。用Windows系统os还特有一个同功能函数os.startfile
sep 路径中使用的分隔符
pathsep 分隔不同路径的分隔符
linesep 行分隔符('\n'、'\r'或'\r\n')
urandom(n) 返回n个字节的强加密随机数据
例如显示环境变量:
'''
import os
os_en=os.environ['PYTHONPATH']
print('^'*80)
print(os_en)
'''
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\\Users\\xx\\AppData\\Local\\Programs\\Python\\Python37\\python.exe;C:\\Users\\xx\AppD
ata\\Local\\Programs\\Python\\Python37\\Scripts\\;C:\\Users\\xx\\AppData\\Local\\Programs\\
Python\\Python37
------------------
(program exited with code: 0)
请按任意键继续. . .
如果出现错误提示,有可能没有设置环境变量的PYTHONPATH参数,可以把PYTHONPATH改为PATH或者设置环境变量添加PYTHONPATH并重启。
在python的程序中路径要用双反斜杠 \\ 表示且要用引号括起来,。如果不这样做,底层shell将受阻于空白处。
在Windows中,使用os.system或os.startfile启动外部程序后,当前Python程序将继续运行;而在UNIX中,当前Python程序将等待命令os.system结束。
函数os.system可用于完成很多任务,但就启动Web浏览器这项任务而言,有一种更佳的解决方案:使用模块webbrowser。这个模块包含一个名为open的函数,让你能够启动启动Web浏览器并打开指定的URL
'''
import webbrowser as we
we.open('http://www.python.org')
'''
将用默认浏览器打开http://www.python.org网页。
10.3.3 fileinput
模块fileinput能够轻松地迭代一系列文本文件中的所有行。
在fileinput模块提供了大量的函数和变量最重要的函数有如下几种:
input([files[, inplace[, backup]]]) 帮助迭代多个输入流中的行
filename() 返回当前文件的名称
lineno() 返回(累计的)当前行号
filelineno() 返回在当前文件中的行号
isfirstline() 检查当前行是否是文件中的第一行
isstdin() 检查最后一行是否来自sys.stdin(标准输出)
nextfile() 关闭当前文件并移到下一个文件
close() 关闭序列
其中input([files[, inplace[, backup]]])函数:
默认格式为:
fileinput.input (files=None, inplace=False, backup='', bufsize=0, mode='r', openhook=None)
其中:
files: #文件的路径列表,默认是stdin方式,多文件['1.txt','2.txt',...]
inplace: #是否将标准输出的结果写回文件,默认不取代
backup: #备份文件的扩展名,只指定扩展名,如.bak。如果该文件的备份文件已存在,则会自动覆盖。
bufsize: #缓冲区大小,默认为0,如果文件很大,可以修改此参数,一般默认即可
mode: #读写模式,默认为只读
openhook: #该钩子用于控制打开的所有文件,比如说编码方式等;
'''
import fileinput as fp
fl=fp.input('xx.txt')
for line in fl:
print(line)
'''
a,
b,
c
d.
ss
and for in
------------------
(program exited with code: 0)
请按任意键继续. . .
10.3.4 集合、堆和双端队列
Python除支持一些较常用的,其中的字典(散列表)和列表(动态数组)之外。还支持一些虽然不那么重要,但有时也能派上用场的数据结构。
1. 集合
集合是由内置类set实现的,这意味着你可直接创建集合,而无需导入模块sets。
'''
s=set(range(10))
print(s)
'''
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
------------------
(program exited with code: 0)
请按任意键继续. . .
可使用序列(或其他可迭代对象)来创建集合,也可使用花括号显式地指定。请注意,不能仅使用花括号来创建空集合,因为这将创建一个空字典。因此创建集合必须用set()。
'''
ss=set()
sd={}
print(type(s))
print(type(ss))
print(type(sd))
'''
<class 'set'>
<class 'set'>
<class 'dict'>
------------------
(program exited with code: 0)
请按任意键继续. . .
集合主要用于成员资格检查,因此将忽略重复的元素。
'''
s1={1,2,3,3,4,5,6,6,7,6,5,4,3,2,1}
print(s1)
'''
{1, 2, 3, 4, 5, 6, 7}
------------------
(program exited with code: 0)
请按任意键继续. . .
与字典一样,集合中元素的排列顺序是不确定的,因此不能依赖于这一点。
除成员资格检查外,还可执行各种标准集合操作(你可能在数学课上学过),如并集和交集,为此可使用对整数执行按位操作的运算符。例如要计算两个集合的并集,可对其中一个集合调用方法union,也可使用按位或运算符|。
'''
a={'a','c','d','e','f'}
b={'a','b','c','g'}
print(a-b) #a中去掉b中与a共有元素
print(a|b) #或(a、b中的所有元素)
print(a&b) #交集集合a和b中都包含了的元素
print(a^b) #异或,不同时包含于a和b的元素
'''
{'f', 'd', 'e'}
{'e', 'f', 'c', 'g', 'b', 'a', 'd'}
{'c', 'a'}
{'e', 'f', 'g', 'b', 'd'}
------------------
(program exited with code: 0)
请按任意键继续. . .
集合是可变的,因此不能用作字典中的键。另一个问题是,集合只能包含不可变(可散列)的值,因此不能包含其他集合。
如遇合并集合问题不能简单的用add函数而是要先把被合并的集合用frozenset函数转化为不可变,然后再用add函数。
啊。
'''
x=set()
y=set()
x.add(y)
'''
Traceback (most recent call last):
File "xx.py", line 8, in <module>
x.add(y)
TypeError: unhashable type: 'set'
------------------
(program exited with code: 1)
请按任意键继续. . .
'''
y1=frozenset(y)
x1=x.add(y1)
print('@'*80)
print(x1)
'''
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
None
------------------
(program exited with code: 0)
请按任意键继续. . .
2. 堆
另一种著名的数据结构是堆(heap),它是一种优先队列。优先队列让你能够以任意顺序添加对象,并随时(可能是在两次添加对象之间)找出(并删除)最小的元素。相比于列表方法min,这样做的效率要高得多。
实际上,Python没有独立的堆类型,而只有一个包含一些堆操作函数的模块。这个模块名为heapq(其中的q表示队列),它包含6个函数,其中前4个与堆操作直接相关。必须使用列表来表示堆对象本身。
heappush(heap, x) 将x压入堆中
heappop(heap) 从堆中弹出最小的元素
heapify(heap) 让列表具备堆特征
heapreplace(heap, x) 弹出最小的元素,并将x压入堆中
nlargest(n, iter) 返回iter中n个最大的元素
nsmallest(n, iter) 返回iter中n个最小的元素
看书中例子:
'''
from heapq import *
from random import shuffle #引入random模块
data = list(range(10))
shuffle(data) #将序列的所有元素随机排序。
heap = []
for n in data:
heappush(heap, n)
print(heap)
heappush(heap, 0.5)
print(heap)
'''
[0, 1, 4, 2, 3, 8, 5, 6, 7, 9]
[0, 0.5, 4, 2, 1, 8, 5, 6, 7, 9, 3]
------------------
(program exited with code: 0)
请按任意键继续. . .
元素的排列顺序并不像看起来那么随意。它们虽然不是严格排序的,但必须保证一点:位置i处的元素总是大于位置i // 2处的元素(反过来说就是小于位置2 * i和2 * i + 1处的元素)。这是底层堆算法的基础,称为堆特征(heap property)。
函数heappop弹出最小的元素(总是位于索引0处),并确保剩余元素中最小的那个位于索引0处(保持堆特征)。虽然弹出列表中第一个元素的效率通常不是很高,但这不是问题,因为heappop会在幕后做些巧妙的移位操作。
还是书上的:
'''
print(heappop(heap))
print(heappop(heap))
print(heappop(heap))
print(heap)
'''
0
0.5
1
[2, 3, 5, 4, 6, 9, 7, 8]
------------------
(program exited with code: 0)
请按任意键继续. . .
函数heapify通过执行尽可能少的移位操作将列表变成合法的堆(即具备堆特征)。如果你的堆并不是使用heappush创建的,应在使用heappush和heappop之前使用这个函数。
继续看:
'''
hea=[100,18,16,17,19,11,22]
heapify(hea)
print(hea)
'''
[11, 17, 16, 18, 19, 100, 22]
------------------
(program exited with code: 0)
请按任意键继续. . .
函数heapreplace用得没有其他函数那么多。它从堆中弹出最小的元素,再压入一个新元素。相比于依次执行函数heappop和heappush,这个函数的效率更高。
'''
heapreplace(hea,0.5)
print(hea)
'''
[0.5, 17, 16, 18, 19, 100, 22]
------------------
(program exited with code: 0)
请按任意键继续. . .
'''
heapreplace(hea,200)
print(hea)
'''
[16, 17, 22, 18, 19, 100, 200]
------------------
(program exited with code: 0)
请按任意键继续. . .
3. 双端队列(及其他集合)
双端队列是指允许两端都可以进行入队和出队操作的队列,其元素的逻辑结构仍是线性结构。将队列的两端分别称为前端和后端,两端都可以入队和出队。
在需要按添加元素的顺序进行删除时,双端队列很有用。在模块collections中,包含类型deque以及其他几个集合(collection)类型。
与集合(set)一样,双端队列也是从可迭代对象创建的,它包含多个很有用的方法。
'''
from collections import deque
q = deque(range(5)) #创建双端序列
q.append(5) #后端添加5
q.appendleft(6) #前端添加6
print(q)
'''
deque([6, 0, 1, 2, 3, 4, 5])
------------------
(program exited with code: 0)
请按任意键继续. . .
双端队列对象还包含方法extend和extendleft,其中extend类似于相应的列表方法,而extendleft类似于appendleft。请注意,用于extendleft的可迭代对象中的元素将按相反的顺序出现在双端队列中。
10.3.5 time
模块time包含用于获取当前时间、操作时间和日期、从字符串中读取日期、将日期格式化为字符串的函数。也可表示为包含9个整数的元组。在元组中的索引顺序为年、月、日、时、分、秒、星期、儒略日、夏令时(0或1)。
秒的取值范围为0~61,这考虑到了闰一秒和闰两秒的情况。
模块time中一些最重要的函数如下:
asctime([tuple]) 将时间元组转换为字符串
localtime([secs]) 将秒数转换为表示当地时间的日期元组
mktime(tuple) 将时间元组转换为当地时间
sleep(secs) 休眠(什么都不做)secs秒
strptime(string[, format])将字符串转换为时间元组
time() 当前时间(从新纪元开始后的秒数,以UTC为准)
(有的是时间讨论时间,先到此为止)
10.3.6 random
模块random包含生成伪随机数的函数,有助于编写模拟程序或生成随机输出的程序。模块中一些重要的函数如下:
random() 返回一个0~1(含)的随机实数
getrandbits(n) 以长整数方式返回n个随机的二进制位
uniform(a, b) 返回一个a~b(含)的随机实数
randrange([start], stop, [step]) 从range(start, stop, step)中随机地选择一个数
choice(seq) 从序列seq中随机地选择一个元素
shuffle(seq[, random]) 就地打乱序列seq
sample(seq, n) 从序列seq中随机地选择n个值不同的元素
看书:
'''
from random import *
from time import *
date1 = (2016, 1, 1, 0, 0, 0, -1, -1, -1)
time1 = mktime(date1)
date2 = (2017, 1, 1, 0, 0, 0, -1, -1, -1)
time2 = mktime(date2)
random_time = uniform(time1, time2)
print(asctime(localtime(random_time)))
'''
Wed Jun 1 05:27:52 2016
------------------
(program exited with code: 0)
请按任意键继续. . .
再看一个时间的例子:
'''
present=time() #获取当前时间
tuple_time=localtime(present)
#在这里不能直接输入汉字,用format插入
str_time=strftime('%Y{y}%m{m}%d{d}%H{h}%M{f}%S{s}',tuple_time).format(y='年',m='月',d='日',h='时',f='分',s='秒')
print(str_time)
'''
2018年11月05日08时55分21秒
------------------
(program exited with code: 0)
请按任意键继续. . .
10.3.7 shelve 和 json
将对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如:XML、JSON或特定格式的字节串)的过程称为序列(编码、调解);反之,则称为反序列化(解码、解调)。shelve 和 json就是用于进行数据序列化的模块,它们提供了几个经常用到的函数:
1、shelve专门用于将Python数据类型的持久化到磁盘,shelf是一个类似dict的对象,操作十分便捷。
提供的常用函数:
open 用于打开一个文件,创建一个 file 对象,相关的方法才可以调用它进行读写。操作完毕(并将所做的修改存盘)时,可调用其方法close关闭文件。
2、json用于实现Python数据类型与通用(json)字符串之间的转换。
提供的常用函数:
i、 dumps()将一个Python数据类型列表进行json格式的编码解析,返回一个str对象。
ii、 dump()将Python内置类型序列化为json对象后写入文件。
iii、 loads()将已编码的JSON字符串编码为Python对象。
iv、 load()将读取json信息。
dump(),load()处理的是json文件,dumps(),loads()处理的是字符串。dump\dumps和load\loads一个是多json文件解码一个是编码。
以上函数参数略。这一节书上介绍的例子与下一章有关,就以后遇到再说吧!
10.3.8 re
模块re提供了对正则表达式的支持。
要掌握正则表达式有点难。关键是每次学习一点点:只考虑完成特定任务所需的知识。预先将所有的知识牢记在心毫无意义。
这里不是介绍正则表达式地方,re模块是用于处理正则表达式的模块,因此只指出re模块中一些重要的函数:
compile(pattern[, flags]) 根据包含正则表达式的字符串创建模式对象
search(pattern, string[, flags]) 在字符串中查找模式
match(pattern, string[, flags]) 在字符串开头匹配模式
split(pattern, string[, maxsplit=0]) 根据模式来分割字符串
findall(pattern, string) 返回一个列表,其中包含字符串中所有与模式匹配的子串
sub(pat, repl, string[, count=0]) 将字符串中与模式pat匹配的子串都替换为repl
escape(string) 对字符串中所有的正则表达式特殊字符都进行转义
group([group1, ...]) 获取与给定子模式(编组)匹配的子串
start([group]) 返回与给定编组匹配的子串的起始位置
end([group]) 返回与给定编组匹配的子串的终止位置(与切片一样,不包含终止位置)
span([group]) 返回与给定编组匹配的子串的起始和终止位置
学好章不容易有的老鸟也要不断学习这章罗列的内容,学习的方法只有一条,多看多练,如果想更深入地学习这章——模块,再次建议浏览“Python库参考手册”(它读起来真的很有趣)然后自己琢磨着多练习,这比看书收获更大!!
'''
(待续)