Python生态工具

一、Python内置小工具

1.1、 1秒钟启动一个下载服务器

在实际工作中,时常会有这样的一个需求:将文件传给其他同事。将文件传给同事本身并不是一个很繁 琐的工作,现在的聊天工具一般都支持文件传输。但是,如果需要传送的文件较多,操作起来就会比较 麻烦。此外,如果文件在远程的服务器上,则需要先将远程服务器的文件下载到本地,然后再通过聊天 工具传给同事。再或者,你并不是特别清楚要传哪几个文件给同事,所以,你们需要进行交流,而交流 的时间成本是比较高的,会降低办事效率。

此时,如果你知道Python内置了一个下载服务器就能够显著提升效率了。例如,你的同事要让你传的文 件位于某一个目录下,那么,你可以进入这个目录,然后执行下面的命令启动一个下载服务器:

python -m SimpleHTTPServer 

在Python 3中,由于对系统库进行了重新整理,因此,使用方式会有不同:

python -m http.server 

执行上面的命令就会在当前目录下启动一个文件下载服务器,默认打开8000端口。完成以后,只需要将 IP和端口告诉同事,让同事自己去操作即可,非常方便高效。

使用浏览器访问Python启动的下载服务器,可以看到一个类似于FTP下载的界面,这个时候单击文件下 载即可。通过这种方式传输文件,可以降低大家的沟通成本,提高文件传输的效率。

上面使用的Python语句,从工作原理来说,仅仅是启动了一个Python内置的Web服务器。如果当前目 录下存在一个名为index.html的文件,则默认显示该文件的内容。如果当前目录下不存在这样一个文 件,则默认显示当前目录下的文件列表,也就是大家看到的下载服务器。

1.2、字符串转换为JSON

JSON是一种轻量级的数据交换格式,易于人类阅读和编写,同时也易于机器解析和生成。由于JSON的 诸多优点,已被广泛使用在各个系统中。JSON使用越广泛,需要将JSON字符串转换为JSON对象的需求 就越频繁。

例如,在工作过程中,我们的系统会调用底层服务的API。底层服务的API一般都是以JSON的格式返 回,为了便于问题追踪,我们会将API返回的JSON转换为字符串记录到日志文件中。当需要分析问题 时,就需要将日志文件中的JSON字符串拿出来进行分析。这个时候,需要将一个JSON字符串转换为 JSON对象,以提高日志的可读性。

这个需求十分常见,以至于使用搜索引擎搜索"JSON",处于搜索结果的第一项便是“在线JSON格式化工 具”。除了打开浏览器,使用在线JSON格式化工具以外,我们也可以使用命令行终端的Python解释器来 解析JSON串,如下所示

[root@oracle ~]# echo '{"address": {"province": "zhejiang", "city": "hangzhou"}, "name": "lmx", "sex": "male"}' | python -m json.tool 
{"address": "city": "hangzhou","province": "zhejiang},"name": "lmx""sex": "male"
}

使用命令行解释器解析JSON串非常方便,而且,为了便于阅读,该工具还会自动将转换的结果进行对 齐和格式化。如下所示:

[root@oracle ~]# echo '{"address": {"province": "zhejiang", "city":
"hangzhou"}, "name": "lmx", "sex": "male"}' | python -m json.tool
{"address": {"city": "hangzhou","province": "zhejiang"},"name": "lmx","sex": "male"
}

1.3、检查第三方库是否正常安装

安装完Python的第三方库以后,如何确认这个库已经正确安装了呢?答案很简单,只需要尝试进行 import导入即可。如果导入没有任何错误,则认为安装成功;如果导入失败,则认为安装失败。

[root@oracle ~]# python
Python 2.7.5 (default, Oct 30 2018, 23:45:53)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

验证Python的第三方库是否安装成功,本身也是一件很简单的事情,但是,如果我们使用脚本对大批量 的服务器进行自动部署,又应该如何验证第三方库安装成功了呢?肯定不能登录每一台服务器进行验 证。这个时候,我们可以使用Python解释器的-c参数快速地执行import语句,如下所示:

[root@oracle ~]# python -c "import paramiko"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named paramiko

二、pip高级用法

为了便于用户安装和管理第三方库和软件,越来越多的编程语言拥有自己的包管理工具,如nodejs的 npm,ruby的gem。Python也不例外,现在Python生态主流的包管理工具是pip

2.1、pip介绍

pip是一个用来安装和管理Python包的工具,是easy_install的替代品,如果读者使用的是Python 2.7.9+或Python 3.4+版本的Python,则已经内置了pip,无须安装直接使用即可。如果系统中没有安装 pip,也可以手动安装

2.2、python3安装pip

方法1:python33安装完成后默认已经带有pip3

[root@oracle bin]# pip3 -V
pip 19.2.3 from /usr/local/python38/lib/python3.8/site-packages/pip (python
3.8)
[root@oracle bin]# pwd
/usr/local/python38/bin

你可以用以下命令,创建软链接

ln -s /usr/local/python38/bin/pip3 /usr/bin/pip3

方法2:使用以下方法重新安装pip插件

下载get-pip.py脚本

wget https://bootstrap.pypa.io/3.2/get-pip.py

运行脚本

python3 get-pip.py

python3创建pip3索引

ln -s /usr/python3.6.1/bin/pip /usr/bin/pip3

测试是否安装成功

pip3 install requests

pip之所以能够成为最流行的包管理工具,并不是因为它被Python官方作为默认的包管理器,而是因为 它自身的诸多优点。pip的优点有:

pip提供了丰富的功能,其竞争对手easy_install则只支持安装,没有提供卸载和显示已安装列表的功
能;
pip能够很好地支持虚拟环境;
pip可以通过requirements.txt集中管理依赖;
pip能够处理二进制格式(.whl);
pip是先下载后安装,如果安装失败,也会清理干净,不会留下一个中间状态。

如果用户没有将软件打包上传到pypi.python.org,则无法使用pip进行安装。对于这种情况,Python生 态也有标准的做法,例如,我们尝试从源码安装paramiko。需要注意的是,我们也可以通过pip安装 paramiko的,这里只是为了演示Python生态中源码安装:

$ git clone https://github.com/paramiko/paramiko.git
$ cd paramiko
$ python setup.py install

2.3、给pip3重命名

切换至家目录,通过.bashrc添加别名

[root@oracle bin]# cd ~
[root@localhost ~]# vim .bashrc
alias pip=pip3
[root@localhost ~]# source .bashrc
[root@localhost ~]# pip -V
pip 19.2.3 from /usr/local/python38/lib/python3.8/site-packages/pip (python
3.8)

2.4、pip3常用命令

子命令 解释说明
install 安装软件包
download 下载软件包
uninstall 卸载安装包
freeze 按照requirements格式输出安装包,可以到其他服务器上执行pip install -r requirements.txt直接安装软件
list 列出当前系统中的安装包
show 查看安装包的信息,包括版本、依赖、许可证、作者、主页等信息
check 检查安装包依赖是否完整
search 查找安装包
wheel 打包软件到wheel格式
hash 计算安装包的hash值
completion 生成命令补全配置
help 获取pip和子命令的帮助信息

2.5、加速pip安装的技巧

如果大家使用Python的时间比较长的话,会发现Python安装的一个问题,即pypi.python.org不是特别 稳定,有时候会很慢,甚至处于完全不可用的状态。这个问题有什么好办法可以解决呢?根据笔者的经 验,至少有两种不同的方法。

1、使用豆瓣或阿里云的源加速软件安装

访问pypi.python.org不稳定的主要原因是因为网络不稳定,如果我们从网络稳定的服务器下载安装 包,问题就迎刃而解了。我们国内目前有多个pypi镜像,推荐使用豆瓣的镜像源或阿里的镜像源。如果 要使用第三方的源,只需要在安装时,通过pip命令的-i选项指定镜像源即可。如下所示:

pip install -i https://pypi.douban.com/simple/ flask

每次都要指定镜像源的地址比较麻烦,我们也可以修改pip的配置文件,将镜像源写入配置文件中。对 于Linux系统来说,需要创建~/.pip/pip.conf文件,然后在文件中保存如下内容:

[root@localhost ~]# mkdir .pip
[root@localhost ~]# cd .pip
[root@localhost .pip]# touch pip.conf
[root@localhost .pip]# vim pip.conf
[global]
index-url = https://pypi.douban.com/simple/
2、将软件下载到本地部署

如果需要对大批量的服务器安装软件包,并且安装包比较多或者比较大,则可以考虑将软件包下载到本 地,然后从本地安装。这对于使用脚本部署大量的服务器非常有用,此外,对于服务器无法连接外网的 情况,也可以使用这种方法。如下所示:

# 下载到本地
pip install --download='pwd' -r requirements.txt# 本地安装
pip install --no-index -f file://'pwd' -r requirements.txt

使用这种方式,只需要下载一次,就可以多处安装,不用担心网络不稳定的问题。并且,pip能够自动 处理软件依赖问题。例如,我们通过这种方式下载Flask到当前目录下,则Flask的依赖click、 itsdangerous、Jinja2、MarkupSafe和Werkzeug也会被下载到本地,如下所示:

pip install --download='pwd' flask$ ls
click-6.7-py2.py3-none-any.whl itsdangerous-0.24.tar.gz
MarkupSafe-0.23.tar.gz Flask-0.12-py2.py3-none-any.whl
Jinja2-2.9.5-py2.py3-none-any.whl Werkzeug-0.11.15-py2.py3-none-any.whl

三、Python变成辅助工具

因为Python是一门动态类型语言,所以,Python程序不需要编译和链接就可以直接运行。Python程序 运行时是从上至下逐行执行,因此Python工程师可以进行交互式的编程,从而快速验证代码的运行结果 是否符合预期。同时,Python工程师也可以通过交互式编程的方式学习Python编程。也正是因为 Python交互式编程的诸多优点,所以,Python交互式编程使用非常广泛。

3.1、Python交互式编程

要使用Python的交互式编程,最简单的方式是使用标准的Python Shell。在命令行直接输入python命 令便可进入Python Shell,如下所示:

[root@localhost ~]# python
Python 3.8.1 (default, Jan 14 2020, 12:20:36)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a=3
>>> b=4
>>> a+b
7

虽然标准的Python Shell也支持交互式编程,但是,它有很多不足,包括:

没有语法高亮;
不支持Tab自动补全;
没有自动缩进功能;
不能保存历史记录;
不能很好地与操作系统交互;
无法导入外部文件中的程序。

虽然Python自带的交互式编程满足了功能性需求,但是在易用性上仍有诸多不足。IPython是增强型的 Python Shell,不但解决了上面提到的各种问题,而且提供了非常丰富的组件,可以方便地进行交互式 编程和数据分析。IPython功能丰富,不可避免地导致软件变得庞大复杂,因此,IPython 4.0对 IPython进行了拆分,分离成IPython Shell和jupyter两个组件,这两个组件现在需要分别安装。 按照行业惯例,IPython代指IPython Shell,是一个类似于Python Shell的交互式解释器;jupyter代指 IPython Notebook,是一个带图形界面的应用程序。接下来我们分别介绍IPython和jupyter的使用。

3.2、使用IPython交互编程

IPython是一个第三方工具,因此,在使用之前需要先安装。可以直接使用操作系统的包管理工具或pip 进行安装。以下是在centos7上的安装方式:

[root@localhost ~]# pip install ipython
Looking in indexes: https://pypi.douban.com/simple/
Collecting ipythonDownloading
https://pypi.doubanio.com/packages/1c/f3/c8be38ee117d02508bb8b9158eb41ca416
f442a6e8e3b3159c2f2d14ed79/ipython-7.11.1-py3-none-any.whl (777kB)|████████████████████████████████| 778kB 934kB/s……省略部分输出信息Installing collected packages: six, ipython-genutils, decorator, traitlets,
ptyprocess, pexpect, pickleshare, wcwidth, prompt-toolkit, pygments,
backcall, parso, jedi, ipythonRunning setup.py install for backcall ... done
Successfully installed backcall-0.1.0 decorator-4.4.1 ipython-7.11.1
ipython-genutils-0.2.0 jedi-0.15.2 parso-0.5.2 pexpect-4.7.0 pickleshare0.7.5 prompt-toolkit-3.0.2 ptyprocess-0.6.0 pygments-2.5.2 six-1.13.0
traitlets-4.3.3 wcwidth-0.1.8

安装完成以后,在命令行终端输入ipython就进入了IPython交互式编程界面:

[root@localhost ~]# ipython
Python 3.8.1 (default, Jan 14 2020, 12:20:36)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.11.1 -- An enhanced Interactive Python. Type '?' for help.In [1]: sum=0In [2]: for i in range(5):...: sum+=i...: print(sum)
10In [3]: import osIn [4]: os.getlogin()Out[4]: 'root'In [5]:

与标准的Python Shell一样,IPython的行显示了所使用的Python解释器版本以及当前的时间。第二行 是获取版权信息的方式,接着给出了IPython的版本。后是简短的使用说明,包括特征介绍、简短的 使用手册和如何获取帮助信息。表2-2给出了IPython提供的使用说明。 接下来我们将从五个不同的维度介绍IPython的使用,分别是:

①更好的编辑器;

②更方便地获取帮助信息;

③IPython提供的magic函数;

④IPython的保存历史功能;

⑤IPython与操作系统交

(1)更好的编辑器

IPython非常强大,有各种高级功能。其中,有用也直观的便是作为交互式编程工具的编辑器功 能。简单来说,IPython相对于标准的Python Shell是一个更好的交互式编程的编辑器,因为它具有:

语法高亮; 
自动缩进; 
Tab补全; 
快速获取帮助信息; 
搜索历史; 
执行shell命令

如果只是描述IPython的特征,相信读者并没有完全的概念。这个时候可以坐在计算机旁,打开IPython 随便敲几行Python代码和标准的Python Shell进行比较,就能够直观感受到IPython的优点。

IPython与标准Python Shell的大区别在于,IPython会对命令提示符的每一行进行编号,编号以后能 够提高交互式编程的可读性。更重要的是,我们可以通过IPython提供的特殊函数对编号以后的代码进 行操作。此外,IPython支持语法高亮和自动缩进,相对于标准的Python Shell,是一个更好的编辑 器。如果在编写代码的过错中出现了错误需要删除时,标准的Python Shell无法进行很好的处理,只能 重新进行输入,而IPython则不存在这样的问题。

tab补全是一个特别有用的功能,IPython支持tab补全,而标准的Python Shell不支持。大家可以想象 一下,一个工程师近正在学习Python,他知道一个库里面有他想要的函数,但是,他并不能非常准确 地说出这个函数的名称。这个时候,如果没有tab补全,就只能一边打开Python官方的参考手册,一边 学习编程。有了tab补全以后,即使他对函数名称不是特别熟悉也没有关系,可以先通过tab补全列出当 前命名空间下的函数列表,然后根据函数名称选择自己需要的函数。IPython的补全功能非常强大,不 但可以补全用户的变量名、标准库的函数,在导入包时也可以进行补全。

这一小节,我们一直在强调IPython比标准的Python Shell更好用,拥有更多高级功能。如果读者接触 Python的时间不长,也许不能理解为什么需要使用交互式编程。交互式编程在当前会话退出以后就结束 了,并不满足计算机程序一次编写多次运行的特点。但是,在我们的日常工作中还是会经常用到交互式编程。

交互式编程不但可以快速验证代码执行结果,还可以帮助我们学习Python编程。Python工程师在编写 代码时,通常会使用编辑器和Python Shell组合的方式来完成程序的编写,例如,将代码从编辑器复制 到Python Shell以验证代码的正确性,然后将验证过的代码从Python Shell复制到编辑器中。

(2)使用IPython来解析MySQL的备份日志

为了便于读者理解交互式编程的好处,我们这里演示一个使用Python交互式编程的例子。在这个例子 中,我们使用IPython来解析MySQL的备份日志。

一个典型的MySQL物理备份日志如下所示:

170221 01:07:48 Executing UNLOCK TABLES
170221 01:07:48 All tables unlocked
Starting slave SQL thread
170221 01:07:48 [00] Streaming ib_buffer_pool to <STDOUT>
170221 01:07:48 [00] ...done
170221 01:07:48 Backup created in directory '/home/lmx/log/backup'
MySQL binlog position: filename 'mysql-bin.000003', position '507946128',
GTID of the last change '5a81ea97-daf1-11e6-94c1-fa163ee35df3:1-3409440'
MySQL slave binlog position: master host '10.173.33.35', filename 'mysqlbin.000002', position '524993060'
170221 01:07:48 [00] Streaming backup-my.cnf
170221 01:07:48 [00] ...done
170221 01:07:48 [00] Streaming xtrabackup_info
170221 01:07:48 [00] ...done
xtrabackup: Transaction log of lsn (3387315364) to (3451223966) was copied.
170221 01:07:48 completed OK!

即使读者对MySQL不了解也没有关系,我们现在的需求是解析下面这一行日志,并获取日志中的host、 filename和position的值。虽然在日志中position的值包含在一对单引号内,但是,我们希望解析以后 position的值是一个整数。

MySQL slave binlog position: master host '10.173.33.35', filename 'mysqlbin.000002', position '524993060'

在这个例子中,主要就是对字符串进行处理,并提取相应的值。这个问题当然不难,但是,如果不借助 交互式编程工具,需要工程师一次在代码中编写正确也不简单。如果工程师不知道交互式编程工具,就 只能在编辑器里面编写代码,然后运行。如果有错误再修改,直到获取正确的取值,整个过程将会非常 耗时。如果项目庞大,调试起来也会比较困难。这个时候就可以借助Python的交互式编程工具,先验证 代码的正确性,然后将验证过的代码从交互式编程工具复制到编辑器中。如下所示:

[root@localhost ~]# ipython
Python 3.8.1 (default, Jan 14 2020, 12:20:36)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.11.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: line="MySQL slave binlog position: master host '10.173.33.35',
filename 'mysql-bin.000002', position '524993060'"
In [2]: line.split("'")
Out[2]:
['MySQL slave binlog position: master host ',
'10.173.33.35',
', filename ',
'mysql-bin.000002',
', position ',
'524993060',
'']
In [3]: host=line.split("'")[1]
In [4]: filename=line.split("'")[3]
In [5]: position=line.split("'")[5]
In [6]: print(host,filename,position)
10.173.33.35 mysql-bin.000002 524993060
In [7]: type(position)
Out[7]: str
In [8]: position=int(position)
In [9]: print(host,filename,position)
10.173.33.35 mysql-bin.000002 524993060

为了节省文章篇幅,我们没有进行错误的尝试,而是直接通过单引号来分解字符串。由于我们使用了交 互式编程,可以很方便地看到字符串分解以后的中间结果。正是有了这个中间结果,我们才知道,字符 串分解成列表以后,下标1对应的字符串是host的值,下标3对应的字符串是filename的值,下标5对应 的字符串是position的值。我们还可以通过交互式编程发现position是一个字符串。由于我们要求 position是一个整数,因此,需要在代码中将position强制转换为一个整数。

如果读者自己尝试从这一行字符串中获取有效的值,很可能一开始会尝试使用逗号或空格来分解字符 串。这两种方法都无法一次取出host、filename和position的值。如下所示:

In [10]: line.split(',')
Out[10]:
["MySQL slave binlog position: master host '10.173.33.35'",
" filename 'mysql-bin.000002'",
" position '524993060'"]In [11]: line.split(' ')
Out[11]:
['MySQL',
'slave',
'binlog',
'position:',
'master',
'host',
"'10.173.33.35',",
'filename',
"'mysql-bin.000002',",
'position',
"'524993060'"]

使用交互式编程,我们可以快速尝试不同的方案,先验证自己的想法是否正确,然后将代码拷贝到编辑 器中,组成我们的Python程序文件。通过这种方式,能够有效降低代码出错的概率,减少调试的时间, 从而提高工作效率。

(3)更好地获取帮助信息

Python工程师不但可以通过交互式编程快速验证代码执行结果,还可以通过交互式编程的方式学习 Python编程。之所以说Python工程师可以通过交互式编程学习编程,是因为使用IPython能够方便地获 取到相应的帮助信息。如命名空间下的每个对象以及其定义和使用说明。虽然标准的Python Shell也可 以通过help函数获取到对象的帮助信息,但是,IPython提供了更加灵活的方式获取命名空间下的对象 列表,以及更加全面的帮助信息。

我们知道,在标准库的os模块下的path子模块中有很多操作文件、目录和路径的函数,也有很多 以"is"开始的判断类函数。这些判断类函数的作用非常明确,用以判断给定的对象是否为一个文件或一 个目录。我们可以使用通配符的方式获取该模块下的所有判断类函数,如下所示:

In [10]: import os
In [11]: ?os.path.is*
os.path.isabs
os.path.isdir
os.path.isfile
os.path.islink
os.path.ismount

获取当前命名空间下的所有对象,除了使用通配符的方式以外,也可以使用前面介绍的tab补全方式。 tab补全的方式更加实用一些,就如同IPython提供的获取帮助信息的方式比标准的Python Shell获取帮 助信息更实用一样。在IPython中,可以通过标准的help函数获取对象的帮助信息,也可以使用“?”和 “??”获取对象的帮助信息,如下所示:

In [12]: import json
In [13]: import os
In [14]: os.path.isfile?
Signature: os.path.isfile(path)
Docstring: Test whether a path is a regular file
File: ~/.pyenv/versions/3.8.1/lib/python3.8/genericpath.py
Type: functionIn [15]: json.dump?Signature:
json.dump(
obj,
fp,
*,
skipkeys=False,
ensure_ascii=True,
check_circular=True,
allow_nan=True,
cls=None,
indent=None,
separators=None,
default=None,
sort_keys=False,
**kw,
)
Docstring:
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object).If ``skipkeys`` is true then ``dict`` keys that are not basic types
(``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
instead of raising a ``TypeError``.

当我们输入对象名称,再输入一个问号以后按回车键,就会显示相应的帮助信息。如果帮助信息比较 长,则会以分页的方式显示帮助信息。如果因为帮助信息太多而进入了分页页面,可以通过“q”键退出, 退出以后可以继续进行编程。

例如,json这个标准库下有一个dump函数和一个dumps函数,Python初学者总是容易混淆。这个时 候,如果能够充分利用IPython,就可以方便地获取到帮助信息,使用时不容易犯错。下面就是一个典 型的Python工程师使用json模块的方式,先构造了一个字典,希望将字典转换成json字符串。因为不知 道应该使用json.dump函数还是json.dumps函数,所以,在交互式编程中通过“json.dump?”语句获取dump函数的帮助信息。获取完json.dump函数的帮助信息以后,按“q”键退出,退出以后继续进行编 程。如下所示:

In [18]: import json
In [19]: d=dict(a=1,b=2,c=3)
In [20]: json.dump?
In [21]: json.dumps(d)
Out[21]: '{"a": 1, "b": 2, "c": 3}'

在IPython中,除了使用一个问号获取帮助信息以外,也可以使用两个问号获取帮助信息。两个问号获 取到的帮助信息更加全面,甚至会包含函数的实现源码。

除了使用问号的方式获取对象的帮助信息以外,IPython还提供了另外一种方式获取对象的信息,可以 分别获取对象的定义、文档和文件等。如下所示:

In [22]: import json
In [23]: %pdef json
Object is not callable.
In [24]: %pdef json.dump
json.dump(
obj,
fp,
*,
skipkeys=False,
ensure_ascii=True,
check_circular=True,
allow_nan=True,
cls=None,
indent=None,
separators=None,
default=None,
sort_keys=False,
**kw,
)
In [25]: %pfile json.dump
In [26]: %pdoc json.dump
In [27]: %pinfo json
(4)magic函数

IPython提供了很多功能强大的函数,如前面已经提到的%pfile、%pdoc、%pinfo等。为了区分 IPython提供的函数和用户的输入,所有IPython提供的函数都以“%”开头。以“%”开头的这类功能强大的 函数,在IPython中称为magic函数。magic函数主要是为IPython提供增强的功能、与操作系统交互、 操纵用户的输入和输出以及对IPython进行配置。

IPython会将任何第一个字母为“%”的行,视为对magic函数的特殊调用。因此,所有的magic函数都是 以“%”开头。在IPython中,有两种不同的方法可以获取magic函数列表,分别是通过“%”获取所有的 magic函数和通过“%lsmagic”获取所有的magic函数。

下面是一个用lsmagic函数获取magic函数列表的例子:

In [28]: %lsmagic
Out[28]:
Available line magics:
%alias %alias_magic %autoawait %autocall %autoindent %automagic
%bookmark %cat %cd %clear %colors %conda %config %cp %cpaste
%debug %dhist %dirs %doctest_mode %ed %edit %env %gui %hist
%history %killbgscripts %ldir %less %lf %lk %ll %load %load_ext
%loadpy %logoff %logon %logstart %logstate %logstop %ls %lsmagic
%lx %macro %magic %man %matplotlib %mkdir %more %mv %notebook
%page %paste %pastebin %pdb %pdef %pdoc %pfile %pinfo %pinfo2 %pip
%popd %pprint %precision %prun %psearch %psource %pushd %pwd %pycat
%pylab %quickref %recall %rehashx %reload_ext %rep %rerun %reset
%reset_selective %rm %rmdir %run %save %sc %set_env %store %sx
%system %tb %time %timeit %unalias %unload_ext %who %who_ls %whos
%xdel %xmodeAvailable cell magics:
%%! %%HTML %%SVG %%bash %%capture %%debug %%file %%html %%javascript
%%js %%latex %%markdown %%perl %%prun %%pypy %%python %%python2
%%python3 %%ruby %%script %%sh %%svg %%sx %%system %%time %%timeit
%%writefileAutomagic is ON, % prefix IS NOT needed for line magics.

可以看到,IPython提供了很多magic函数。并且,随着IPython的功能越来越多,magic函数还会不断 增加。那么,有没有一种好的方法能够快速了解magic函数的用法呢?前面介绍的通过问号获取对象帮 助信息的方法对magic函数也适用。因此,只要输入一个magic函数,后面再输入一个问号,回车以后 就能够看到这个magic函数的帮助信息。如下所示:

In [29]: %save?
Docstring:
Save a set of lines or a macro to a given filename.Usage:%save [options] filename n1-n2 n3-n4 ... n5 .. n6 ...Options:-r: use 'raw' input. By default, the 'processed' history is 	used,so that magics are loaded in their transformed version 	to validPython. If this option is given, the raw input as 		typed as thecommand line is used instead.-f: force overwrite. If file exists, %save will prompt for 		overwriteunless -f is given.-a: append to the file instead of overwriting it.
:

IPython的官方文档将magic函数分为三类,分别是:

1)操作代码的magic函数,如%run、%edit、%save、%macro、%recall;
2)控制IPython的magic函数,如%colors、%xmode、%autoindent、%automagic;
3)其他magic函数,如%reset、%timeit、%%writefile、%load、%paste。

为了演示magic函数的使用,我们来看一个实际的例子。假设你是一名DBA,并且非常喜欢Python这门 编程语言,会经常使用Python管理MySQL。因此,你经常需要使用Python连接MySQL执行SQL语句 (Python连接MySQL的知识将在11章介绍)。使用Python执行SQL语句,对于普通的查询语句,返回 的结果将是一个二维的元组。但是,如果执行的是一些管理类的SQL语句或者监控类的SQL语句, Python驱动将会以怎样的方式返回MySQL的查询结果呢?

例如,需要执行下面的SQL语句,并获取返回结果:

show slave statusshow master statusshow variables like '%innodb%buffer%'show status like '%select%'set global innodb_buffer_pool_dump_pct = 30GRANT ALL PRIVILEGES ON . TO ['lmx'@'localhost'](mailto:'lmx'@'localhost')
WITH GRANT OPTION

为了得到Python执行上面SQL语句的结果,需要在Python中连接MySQL并进行认证。认证完成以后执 行SQL语句获取输出。由于你经常需要验证SQL语句,因此,使用Python连接MySQL并认证这些代码需 要反复输入。为了节省输入时间,我们可以将Python连接MySQL并认证的逻辑保存到外部文件中,在 需要的时候通过%load这个magic函数将外部代码导入到IPython中执行即可。例如,我们在一个名为 connect.py的外部文件中保存了连接MySQL的代码,在Ipython中使用%load导入外部Python文件:

In [30]: %load connect.pyIn [31]: import MySQLdb as db
conn = db.connect(host="localhost", db="test", user='lmx',
passwd='my_passwd', unix_socket='/tmp/mysql.sock')
cur = conn.cursor()
sql = "select 1"
cur.execute(sql)
rows = cur.fetchall()
print rows
((1L,),)

使用%load命令导入外部的Python文件并执行以后,可以继续使用已经建立的MySQL连接执行SQL语 句。这个例子主要用以演示magic函数的用法,IPython提供了大量的magic函数,每一个magic函数的 具体用法都可以通过问号表达式获取相应的帮助文档。

(5)保存历史

保存编码历史这方面,IPython相比标准的Python Shell有了质的提升。用户可以非常灵活地操作 IPython的输入历史和输出历史。下面我们简单看几个例子:

_i, _ii, _iii 分别保存了最近的三次输入; _, , _ 分别保存了最近的三次输出; 可以像Bash一样,通 过ctrl+p, ctrl+n查找输入; 可以像Bash一样,使用ctrl+r进行反向查找; IPython的输入历史在 当前会话退出以后会进行持久化,下一次进入IPython时,依然可以查找前一次会话的输入历史; %edit IPython可以通过%edit编辑历史输入并重新执行; %save IPython可以通过%save将 IPython中的代码保存到程序文件中; %rerun IPython可以指定代码行数重新运行;

In [32]: %rerun 21
=== Executing: ===
json.dumps(d)
=== Output: ===
Out[32]: '{"a": 1, "b": 2, "c": 3}'
(6)与操作系统交互

IPython比标准的Python Shell好用的另一个理由是,它能够更好地与操作系统进行交互。在使用 Python进行交互式编程时,不用退出Python Shell就可以执行Linux命令。magic函数里的%cd和%pwd 作用相当于Linux下的cd命令和pwd命令。此外,在IPython中,可以通过“!cmd”的形式执行任何Linux 命令。如下所示:

In [33]: %ls
anaconda-ks.cfg initial-setup-ks.cfg Python-3.8.1/ Python-3.8.1.tgz
In [34]: %pwd
Out[34]: '/root'
In [35]: ! wc -l /tmp/storage.log
0 /tmp/storage.log

也可以通过赋值的方式捕获命令的输出:

In [36]: data=!df
In [37]: data
Out[37]:
['文件系统 			   1K-块    	  已用      可用       已用%      挂载点',
'/dev/mapper/centos-root 17811456 		6060988  11750468   35%  	/',
'devtmpfs 			480872               0  480872     0%    	/dev',
'tmpfs 				497948               0  497948     0% 		/dev/shm',
'tmpfs                   497948            8696  489252     2% 		/run',
'tmpfs                   497948               0  497948     0% 	     /sys/fs/cgroup',
'/dev/sda1               1038336         169504  868832     17% 		/boot',
'tmpfs                   99592               64  99528      1% 	     /run/user/1000',
'tmpfs                   99592                0  99592      0% 		/run/user/0']
In [38]: data[1].split()[4]
Out[38]: '35%'

在Python生态中,除了IPython这个增强的Python Shell以外,还有bython和ptpython这两个不错的 Python Shell。后面这两个工具都有自己的特色,但是都没有IPython使用广泛。而且,由于IPython使 用最为广泛,很多开源项目(如流行的爬虫框架Scrapy)对IPython进行了集成,所以,建议读者学习 IPython。

3.3、 jupyter的使用

(1)、jupyter介绍

jupyter就是以前的IPython Notebook,是一种新兴的交互式数据分析与记录工具。它通过浏览器访问 本地或者远端的IPython进程,并利用浏览器的图形界面,增强IPython的可视化输出。jupyter定义了 一种全新的文件格式,文件的后缀名是ipynb。ipynb文件包含了代码,用以说明每一步的计算和输出。 也就是说,ipynb文件完整记录了计算过程中的所有相关信息,并且,能够支持图片、视频和公式等副 文本格式,是科学计算、数据分析和编程教学的优秀工具。

正是由于jupyter丰富的可视化输出,其广泛应用于以下场景:

编程教学;
数据分析;
科学计算;
幻灯片演示。
(2)、 jupyter notebook的使用

IPython Shell与jupyter分离以后,jupyter需要额外进行安装。直接使用pip安装即可:

[root@localhost ~]# pip install jupyter
Looking in indexes: https://pypi.douban.com/simple/
Collecting jupyter
Downloading
https://pypi.doubanio.com/packages/83/df/0f5dd132200728a86190397e1ea87cd762
44e42d39ec5e88efd25b2abd7e/jupyter-1.0.0-py2.py3-none-any.whl
……省略部分信息
Successfully installed MarkupSafe-1.1.1 Send2Trash-1.5.0 attrs-19.3.0
bleach-3.1.0 defusedxml-0.6.0 entrypoints-0.3 ipykernel-5.1.3 ipywidgets7.5.1 jinja2-2.10.3 jsonschema-3.2.0 jupyter-1.0.0 jupyter-client-5.3.4
jupyter-console-6.0.0 jupyter-core-4.6.1 mistune-0.8.4 nbconvert-5.6.1
nbformat-5.0.3 notebook-6.0.2 pandocfilters-1.4.2 prometheus-client-0.7.1
prompt-toolkit-2.0.10 pyrsistent-0.15.7 python-dateutil-2.8.1 pyzmq-18.1.1
qtconsole-4.6.0 terminado-0.8.3 testpath-0.4.4 tornado-6.0.3 webencodings0.5.1 widgetsnbextension-3.5.1

由于我们是在Linux下安装jupyter,如果我们的Linux没有图形界面,可以通过设置–no-browser和设 置–ip=0.0.0.0进行外部访问,如果不指定–ip参数,默认IP是localhost,也就是只有本地才能访问。如 下所示:

[root@localhost ~]# jupyter notebook --no-browser --ip=0.0.0.0 --allow-root
[I 13:06:33.656 NotebookApp] 启动notebooks 在本地路径: /root
[I 13:06:33.657 NotebookApp] 本程序运行在: http://localhost:8888/?
token=33fdb8256c7c1d13a23b8189ea0f4e7b7f434f14cc07ea5e
[I 13:06:33.657 NotebookApp] or http://127.0.0.1:8888/?
token=33fdb8256c7c1d13a23b8189ea0f4e7b7f434f14cc07ea5e
[I 13:06:33.657 NotebookApp] 使用control-c停止此服务器并关闭所有内核(两次跳过确认).
[C 13:06:33.727 NotebookApp]To access the notebook, open this file in a browser:file:///root/.local/share/jupyter/runtime/nbserver-3998-open.htmlOr copy and paste one of these URLs:http://localhost:8888/?
token=33fdb8256c7c1d13a23b8189ea0f4e7b7f434f14cc07ea5eor http://127.0.0.1:8888/?
token=33fdb8256c7c1d13a23b8189ea0f4e7b7f434f14cc07ea5e

从jupyter notebook的输出结果可以看到,jupyter notebook命令给出了一个URL,我们只需将该URL 拷贝至浏览器中,然后将0.0.0.0替换为Linux服务器的IP即可。

在Windows下可以使用远程连接工具xmanager来操作

通过浏览器访问jupyter给我们的URL,就可以登录到jupyter的主界面。这个界面会显示当前目录下的 所有文件
Python生态工具-编程知识网

登录jupyter的主界面后,我们如果要创建一个文件,只需要单击“新建”,选择你希望启动的Notebook 类型即可。我们选择Python 3。选择Python 3以后,浏览器会打开一个新的页面。在这个新的页面中, 可以看到一个空的Notebook界面。

jupyter界面由以下部分组成:

标题栏
菜单栏
快捷键
编辑区

在菜单栏中有一个“帮助”选项,读者可以通过该选项得到jupyter的使用说明。jupyter本身是图形界面的 应用,使用比较简单,因此,本教程不会花很多篇幅来介绍jupyter的使用。
Python生态工具-编程知识网

在jupyter的编辑区中默认有一个输入框。输入框在jupyter中称为cell。我们可以通过菜单栏的“cell”选项 控制cell的格式、执行cell的代码。与此同时,我们也可以通过快捷键控制cell,如ctrl+enter快捷键用以 执行cell中的代码,shift+enter快捷键用以执行当前cell中的代码,并且在当前cell下方创建一个新的 cell。
Python生态工具-编程知识网

jupyter之所以能够进行编程教学和幻灯片演示,是因为它可以支持富文本格式和markdown格式。我们 只需修改cell的类型为“Markdown”,就可以在cell中使用markdown语句进行输入了。我们也可以在 jupyter中画图。为了在jupyter中画图,我们需要先安装matplotlib。如下所示:

[root@localhost ~]# pip install matplotlib
Looking in indexes: https://pypi.douban.com/simple/
Collecting matplotlib
Downloading
https://pypi.doubanio.com/packages/53/6c/7b400d45f0ecd6703b2779a7dfda657857
9a353748e1b43d8353cb7f5b7f/matplotlib-3.1.2-cp38-cp38-manylinux1_x86_64.whl
(13.1MB)
|████████████████████████████████| 13.1MB 1.8MB/s
……省略部分信息
Installing collected packages: kiwisolver, numpy, pyparsing, cycler,
matplotlib
Successfully installed cycler-0.10.0 kiwisolver-1.1.0 matplotlib-3.1.2
numpy-1.18.1 pyparsing-2.4.6

安装matplotlib以后就可以在jupyter中画图了,下面给出了一个jupyter使用的例子。

import matplotlib.pyplot as plt
import numpy
x = numpy.arange(11)
y = x**2
plt.plot(x, y)

运行结果如下图:
Python生态工具-编程知识网

四、Python工作环境管理

Python 2和Python 3之间存在着较大的差异,并且,由于各种原因导致了Python 2和Python 3的长期 共存。在实际工作过程中,我们可能会同时用到Python 2和Python 3,因此,需要经常在Python 2和 Python 3之间进行来回切换。此外,如果你是喜欢尝鲜的人,那么,你很有可能在Python新版本出来 的时候立即下载Python的版本,试验Python的特性。

在Python世界里,除了需要对Python的版本进行管理以外,还需要对不同的软件包进行管理。大部分 情况下,对于开源的库我们使用***版本即可。但是,有时候可能需要对相同的Python版本,在不同的 项目中使用不同版本的软件包。

在这一节里,我们将介绍两个工具,即pyenv和virtualenv。前者用于管理不同的Python版本,后者用 于管理不同的工作环境。有了这两个工具,Python相关的版本问题将不再是问题。

4.1、使用pyenv管理不同的Python版本

安装不同的Python版本并不是一件容易的事情,在不同的Python版本之间来回切换更加困难,而且, 多版本并存非常容易互相干扰。因此,我们需要一个名为pyenv的工具。pyenv是一个Python版本管理 工具,它能够进行全局的Python版本切换,也可以为单个项目提供对应的Python版本。使用pyenv以 后,可以在服务器上安装多个不同的Python版本,也可以安装不同的Python实现。不同Python版本之 间的切换也非常简单。接下来我们就一起看一下pyenv的安装和使用。

1、.pyenv的安装

我们直接从GitHub下载项目到本地,然后,分别执行以下命令进行安装即可

[root@localhost .pip]# yum -y install git
[root@localhost .pip]# git clone https://github.com/yyuu/pyenv.git ~/.pyenv正克隆到 '~/.pyenv'...
remote: Enumerating objects: 8, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 17600 (delta 2), reused 2 (delta 0), pack-reused 17592
接收对象中: 100% (17600/17600), 3.44 MiB | 601.00 KiB/s, done.
处理 delta 中: 100% (11954/11954), done.[root@localhost .pip]# echo 'export PYENV_ROOT="$HOME/.pyenv"' >>
~/.bash_profile
[root@localhost .pip]# echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >>
~/.bash_profile
[root@localhost .pip]# echo 'eval "$(pyenv init -)"' >>~/.bash_profile

安装完成以后需要重新载入配置文件,或者退出以后重新登录,以使~/.bash_profile中的配置生效。 笔者一般选择使用source命令重新载入配置文件,如下所示:

[root@localhost .pip]# source ~/.bash_profile

至此,pyenv就安装完成了,我们可以通过下面的命令验证pyenv是否正确安装并获取pyenv的帮助信 息:

[root@localhost ~]# pyenv --help
Usage: pyenv <command> [<args>]Some useful pyenv commands are:commands 	List all available pyenv commandscommands 	List all available pyenv commandsexec 	Run an executable with the selected Python versionglobal 	Set or show the global Python versionhelp 	Display help for a commandhooks 	List hook scripts for a given pyenv commandinit 	Configure the shell environment for pyenvinstall 	Install a Python version using python-buildlocal 	Set or show the local application-specific Python versionprefix 	Display prefix for a Python versionrehash 	Rehash pyenv shims (run this after installing executables)root 	Display the root directory where versions and shims are keptshell 	Set or show the shell-specific Python versionshims 	List existing pyenv shimsuninstall 	Uninstall a specific Python versionversion 	Show the current Python version and its origin--version 	Display the version of pyenvversion-file 	Detect the file that sets the current pyenv versionversion-name 	Show the current Python versionversion-origin 	Explain how the current Python version is setversions 	List all Python versions available to pyenvwhence 	List all Python versions that contain the given executablewhich 	Display the full path to an executableSee `pyenv help <command>' for information on a specific command.
For full documentation, see: https://github.com/pyenv/pyenv#readme
2、pyenv的使用

我们通过pyenv的install命令,可以查看pyenv当前支持哪些Python版本,如下所示

[root@localhost ~]# pyenv install --list
Available versions:
2.1.3
……省略部分信息
3.8.0
3.8-dev
3.8.1
3.9-dev
……省略部分信息
anaconda3-2018.12
anaconda3-2019.03
anaconda3-2019.07
anaconda3-2019.10
……省略部分信息

由于pyenv可以安装的Python版本列表非常长,所以,这里进行了省略。读者可以在自己电脑上安装 pyenv,然后执行pyenv install –list命令进行查看。可以看到,pyenv不但可以安装不同的Python版 本,而且还可以安装不同的Python实现,也可以安装***版本的Python用以学习。

查看当前系统中包含的Python版本:

[root@localhost ~]# pyenv versions
* system (set by /root/.pyenv/version)

使用pyenv安装不同的Python版本:

pyenv install -v 3.6.0
pyenv install -v 2.7.13

再次查看当前系统中包含的Python版本:

[root@localhost ~]# pyenv versions
* system (set by /root/.pyenv/version)
2.7.13
3.8.1

由于我们安装了2个Python版本,加上我们系统自身的Python,当前系统中存在3个不同的Python版 本。其中,输出结果前面的“*”表示当前正在使用的版本。我们也可以通过pyenv global选择不同的 Python版本,如下所示:

[root@localhost ~]# pyenv global 3.8.1
[root@localhost ~]# pyenv versions
system
2.7.13
* 3.8.1 (set by /root/.pyenv/version)
[root@localhost ~]# python
Python 3.8.1 (default, Jan 14 2020, 12:20:36)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()
[root@localhost ~]# pyenv global 2.7.13
[root@localhost ~]# python
Python 2.7.13 (default, Jan 14 2020, 12:27:38)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

使用pyenv以后,可以快速切换Python的版本。切换Python版本以后,与版本相关的依赖也会一起切 换。因此,我们不用担心不同的版本在系统中是否会相互干扰。例如,切换Python版本以后,相应的 pip也会跟着切换,所以不用担心自己使用的pip版本和Python版本不匹配的问题,如下所示:

[root@localhost ~]# pyenv global 3.8.1
[root@localhost ~]# pip --version
pip 19.2.3 from /root/.pyenv/versions/3.8.1/lib/python3.8/site-packages/pip
(python 3.8)

如果想要删除Python版本,使用uninstall命令即可。如下所示:

pyenv uninstall 2.7.10

4.2、 使用virtualenv管理不同的项目

virtualenv本身是一个独立的项目,用以隔离不同项目的工作环境。例如,用户lmx希望在项目A中使用 Flask 0.8这个版本,与此同时,又想在项目B中使用Flask 0.9这个版本。如果我们全局安装Flask,必然 无法满足用户的需求。这个时候,我们就可以使用virtualenv。

读者需要注意pyenv和virtualenv的区别。pyenv用以管理不同的Python版本,例如,你的系统工作时 使用Python 2.7.13,学习时使用Python 3.6.0。virtualenv用以隔离项目的工作环境,例如,项目A和 项目B都是使用Python 2.7.13,但是,项目A需要使用Flask 0.8版本,项目B需要使用Flask 0.9版本。我 们只要组合pyenv和virtualenv这两个工具,就能够构造Python和第三方库的任意版本组合,拥有很好 的灵活性,也避免了项目之间的相互干扰。

virtualenv本身是一个独立的工具,用户可以不使用pyenv而单独使用virtualenv。但是,如果你使用了 pyenv,就需要安装pyenv-virtualenv插件,而不是通过virtualenv软件使用virtualenv的功能。

1、pyenv-virtualenv的安装

安装和使用pyenv-virtualenv插件如下所示:

[root@localhost ~]# git clone https://github.com/yyuu/pyenv-virtualenv.git
$(pyenv root)/plugins/pyenv-virtualenv
正克隆到 '/root/.pyenv/plugins/pyenv-virtualenv'...
remote: Enumerating objects: 2064, done.
remote: Total 2064 (delta 0), reused 0 (delta 0), pack-reused 2064
接收对象中: 100% (2064/2064), 580.31 KiB | 264.00 KiB/s, done.
处理 delta 中: 100% (1413/1413), done.
[root@localhost ~]# echo 'eval "$(pyenv virtualenv-init -)"'
>>~/.bash_profile

与安装pyenv类似,安装完成以后需要重新载入配置文件,或者退出用户再登录,以使得配置文件生 效:

[root@localhost ~]# source ~/.bash_profile
[root@localhost ~]# pyenv help virtualenv
Usage: pyenv virtualenv [-f|--force] [VIRTUALENV_OPTIONS] [version]
<virtualenv-name>pyenv virtualenv --versionpyenv virtualenv --help-f/--force 	Install even if the version appears to be installed
already
2、pyenv-virtualenv的使用

有了pyenv-virtualenv以后,我们可以为同一个Python解释器,创建多个不同的工作环境。例如,我们 新建两个工作环境:

[root@localhost ~]# pyenv virtualenv 3.8.1 first_project
[root@localhost ~]# pyenv virtualenv 3.8.1 second_project

可以使用virtualenvs子命令查看工作环境:

[root@localhost ~]# pyenv virtualenvs3.8.1/envs/first_project (created from /root/.pyenv/versions/3.8.1)3.8.1/envs/second_project (created from 	/root/.pyenv/versions/3.8.1)first_project (created from /root/.pyenv/versions/3.8.1)second_project (created from /root/.pyenv/versions/3.8.1)

创建完工作环境以后,可以通过activate和deactivate子命令进入或退出一个工作环境。进入工作环境 以后,左边的提示符会显示你当前所在的工作环境,以免因为环境太多导致操作错误。

[root@localhost ~]# pyenv activate first_project
pyenv-virtualenv: prompt changing will be removed from future release.
configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1' to simulate the
behavior.
(first_project) [root@localhost ~]# pip install flask==1.1.1
Looking in indexes: https://pypi.douban.com/simple/
Collecting flaskDownloading
https://pypi.doubanio.com/packages/9b/93/628509b8d5dc749656a9641f4caf13540e
2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)|████████████████████████████████| 102kB 4.7MB/s
……省略部分信息
Successfully installed Jinja2-2.10.3 MarkupSafe-1.1.1 Werkzeug-0.16.0
click-7.0 flask-1.1.1 itsdangerous-1.1.0
(first_project) [root@localhost ~]# pyenv deactivate

接下来,我们看一下在不同的工作环境安装不同的Flask版本:

[root@localhost ~]# pyenv activate first_project
(first_project) [root@localhost ~]# pip install flask==1.1.1
(first_project) [root@localhost ~]# pyenv deactivate(second_project) [root@localhost ~]# pip install flask==0.10.1

如果想要删除虚拟环境,则使用:

(first_project) [root@localhost ~]# pyenv virtualenv-delete first_project

使用pyenv和python-virtualenv插件,我们就能够自由地在不同的版本之间进行切换,相比管理Python 版本,不但节省了时间,也避免了工作过程中的相互干扰。