模块
1. 模板定义
到目前为止,我们在 Python 解释器中写的所有代码都在我们退出解释器的时候丢失了。但是当人们编写大型程序的时候他们会倾向于将代码分为多个不同的文件以便使用,调试以及拥有更好的可读性。在 Python 中我们使用模块来到达这些目的。模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py 后缀。
你可以由全局变量 __name__ 得到模块的模块名(一个字符串)。
现在我们来看看模块是怎样工作的。创建一个 bars.py 文件。文件内容如下:Moduletest.py
#! /usr/bin/python #coding=utf-8 ''' Bars Module ======= 这是打印不同分割线的模块 ''' def starbar(num): '''打印*分割线''' print('*'* num) def hashbar(num): '''打印#分割线''' print('#'* num) def simplebar(num): '''打印_分割线''' print('_'* num) def simplebar(num): '''打印_分割线''' print('_'* num)
引用
>>> import Moduletest >>> Moduletest.hashbar(6) ###### >>> Moduletest.simplebar(6) ______ >>> 或者
>>> from Moduletest import simplebar,hashbar >>> hashbar(6) ###### >>> simplebar(6) ______ >>>
#from module import * 导入模块中的所有定义,然而这并不是推荐的做法
2.包
包 定义
为了组织好模块,会将多个模块分为包。Python 处理包也是相当方便的。简单来说,包就是文件夹,但该文件夹下必须存在 __init__.py 文件。
常见的包结构如下:
最简单的情况下,只需要一个空的 __init__.py 文件即可。当然它也可以执行包的初始化代码,或者定义稍后介绍的 __all__ 变量。当然包底下也能包含包,这和文件夹一样,还是比较好理解的。
导入包
包的导入仍使用 import 、 from ... import 语句,使用 “圆点模块名” 的结构化模块命名空间。 下面来看一个包的例子来了解下具体的运作。(官方文档中的例子)
假设你现在想要设计一个模块集(一个“包”)来统一处理声音文件和声音数据。存在几种不同的声音格式(通常由它们的扩展名来标识,例如: .wav, .aiff, .au )于是,为了在不同类型的文件格式之间转换,你需要维护一个不断增长的包集合。可能你还想要对声音数据做很多不同的操作(例如混音,添加回声,应用平衡 功能,创建一个人造效果)所以你要加入一个无限流模块来执行这些操作。你的包可能会是这个样子(通过分级的文件体系来进行分组):
用户可以每次只导入包里的特定模块,例如: import sound.efforts.echo 这样就导入了 sound.effects.echo 子模块。它必须通过完整的名称来引用:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
导入包时有一个可以选择的方式: from sound.effects import echo 这样就加载了 echo 子模块,并且使得它在没有包前缀的情况下也可以使用,所以它可以如下方式调用:
echo.echofilter(input, output, delay=0.7, atten=4)
还有另一种变体用于直接导入函数或变量: from sound.effects.echo import echofilter 这样就又一次加载了 echo 字模块,但这样就可以直接调用它的 echofilter() 函数:
echo.echofilter(input, output, delay=0.7, atten=4)
需要注意的是 from package import item 方式导入包时,这个子项(item)既可以是子包也可以是其他命名,如函数、类、变量等。若无,会引发ImportError异常。
而用类似 import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是类、函数、变量等。
从 * 导入包
import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。这可能会花长时间,并出现边界效应等。Python 解决方案是提供一个明确的包索引。
这个索引由 __init__.py 定义 __all__ 变量,该变量为一列表,如上例 sound/effects 下的 __init__.py 中,可定义 __all__ = ["echo","surround","reverse"]
这意味着, from sound.effects import * 会从对应的包中导入以上三个子模块; 尽管提供 import * 的方法,仍不建议在生产代码中使用这种写法。
包内引用
如果是子包内的引用,可以按相对位置引入子模块 以 echo 模块为例,可以引用如下:
1 from . import reverse # 同级目录 导入 reverse 2 from .. import frormats # 上级目录 导入 frormats 3 from ..filters import equalizer # 上级目录的filters模块下 导入 equalizer
3,默认模块
os 模块
os 模块提供了与操作系统相关的功能。你可以使用如下语句导入它:
>>> import os getuid() 函数返回当前进程的有效用户 id。 >>> os.getuid() 500 getpid() 函数返回当前进程的 id。getppid() 返回父进程的 id。 >>> os.getpid() 16150 >>> os.getppid() 14847 uname() 函数返回识别操作系统的不同信息,在 Linux 中它返回的详细信息可以从 uname -a 命令得到。uname() 返回的对象是一个元组,(sysname, nodename, release, version, machine)。 >>> os.uname() ('Linux', 'd80', '2.6.34.7-56.fc13.i686.PAE', '#1 SMP Wed Sep 15 03:27:15 UTC 2010', 'i686') getcwd() 函数返回当前工作目录。chdir(path) 则是更改当前目录到 path。在例子中我们首先看到当前工作目录是 /home/shiyanlou,然后我们更改当前工作目录到 /Code 并再一次查看当前工作目录。 >>> os.getcwd() '/home/shiyanlou' >>> os.chdir('Code') >>> os.getcwd()
'/home/shiyanlou/Code'
所以现在让我们使用 os 模块提供的另一个函数来创建一个自己的函数,它将列出给定目录下的所有文件和目录。
def view_dir(path='.'): """ 这个函数打印给定目录中的所有文件和目录 :args path: 指定目录,默认为当前目录 """ names = os.listdir(path) names.sort() for name in names: print(name, end =' ') print()
使用例子中的 view_dir() 函数。
>>> view_dir('/')
.bashrc .dockerenv .profile bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
os 模块还有许多非常有用的函数,你可以在这里阅读相关内容。
Requests 模块
第三方模块并不是默认的模块,意味着你需要安装它,我们使用 pip3 安装它。
$ sudo pip install requests
上面的命令会在你的系统中安装 Python3 版本的 Requests 模块。
获得一个简单的网页
你可以使用 get() 方法获取任意一个网页。
>>> import requests >>> req = requests.get('https://github.com') >>> req.status_code 200
req 的 text 属性存有服务器返回的 HTML 网页,由于 HTML 文本太长就不在这里贴出来了。
使用这个知识,让我们写一个能够从指定的 URL 中下载文件的程序。
代码写入文件 /home/shiyanlou/download.py:
#!/usr/bin/env python3 import os import os.path import requests def download(url): '''从指定的 URL 中下载文件并存储到当前目录 :arg url: 要下载的文件的 URL ''' req = requests.get(url) # 首先我们检查是否存在文件 if req.status_code == 404: print('No such file found at %s' % url) return filename = url.split('/')[-1] with open(filename, 'wb') as fobj: fobj.write(req.content) print("Download over.") if __name__ == '__main__': url = input('Enter a URL: ') download(url)
测试一下程序:
可以看到目录下已经多了一个 sample.txt 文件。
你可能已经注意到了 if __name__ == '__main__': 这条语句,它的作用是,只有在当前模块名为 __main__ 的时候(即作为脚本执行的时候)才会执行此 if 块内的语句。换句话说,当此文件以模块的形式导入到其它文件中时,if 块内的语句并不会执行。
你可以将上面的程序修改的更友好些。举个例子,你可以检查当前目录是否已存在相同的文件名。os.path 模块可以帮助你完成这个。
argparse 命令行参数处理模块
你还记得 ls 命令吗,你可以传递不同的选项作为命令行参数。
这里是用到的模块是 sys,命令行传入的所有参数都可以使用 sys.argv 获取。如果希望对参数进行处理可以使用 argparse 模块,阅读这篇 文档 学习。