Python不仅语言核心非常强大,还提供了其他工具以供使用。标准安装包含一组称为标准库(standard library)的模块,你见过其中的一些(如math和cmath),但还有其他很多。本章简要介绍模块的工作原理以及如何探索模块以获悉其提供的功能,然后概述标准库,重点是几个很有用的模块。
10.1 模块
10.1.1 模块就是程序
任何Python程序都可作为模块导入。假设你编写了下面的程序,并将其保存在文件hello.py中,这个文件的名称(不包括扩展名.py)将成为模块的名称。
# hello.py
print("Hello, world!")
文件的存储位置也很重要,将在下一节详细介绍。这里假设这个文件存储在目录C:\python(Windows)或~/python(UNIX/macOS)中。
要告诉解释器去哪里查找这个模块,可执行如下命令(以Windows目录为例):
>>> import sys
>>> sys.path.append('C:/python')
这告诉解释器,除了通常将查找的位置外,还应到目录C:\python中去查找这个模块。这样做后,就可以导入这个模块了(它存储在文件C:\python\hello.py中)。
>>> import hello
Hello, world!
如你所见,导入这个模块时,执行了其中的代码。但如果再次导入它,什么事情都不会发生。
>>> import hello
>>>
这次为何没有执行代码呢?因为模块并不是用来执行操作(如打印文本)的,而是用于定义变量、函数、类等。鉴于定义只需做一次,因此导入模块多次和导入一次的效果相同。
10.1.2 模块是用来下定义的
模块在首次被导入程序时执行。这看似有点用,但用处不大。让模块值得被创建的原因在于它们像类一样,有自己的作用域。这意味着在模块中定义的类和函数以及对其进行赋值的变量都将成为模块的属性。这看似复杂,但实际上非常简单。
1. 在模块中定义函数
# hello2.py
def hello():
print("Hello, world!")
现在可以像下面这样导入它:
import hello2
这将执行这个模块,也就是在这个模块的作用域内定义函数hello,因此可像下面这样访问这个函数:
>>> hello2.hello()
Hello, world!
在模块的全局作用域内定义的名称都可像上面这样访问。为何要这样做呢?为何不在主程序中定义一切呢?
主要是为了重用代码。通过将代码放在模块中,就可在多个程序中使用它们。这意味着如果你编写了一个出色的客户数据库,并将其放在模块clientdb中,就可在记账时、发送垃圾邮件(但愿你不会这样做)时以及任何需要访问客户数据的程序中使用它。如果没有在独立的模块中,就需在每个这样的程序中重新编写它。因此,要让代码是可重用的,务必将其模块化!(这也与抽象紧密相关。)
2. 在模块中添加测试代码
是检查模块是作为程序运行还是被导入另一个程序。为此,需要使用变量name。
>>> __name__
'__main__'
>>> hello2.__name__
'hello2'
如你所见,在主程序中(包括解释器的交互式提示符),变量name的是'main',而在导入的模块中,这个变量被设置为该模块的名称。因此,要让模块中测试代码的行为更合理,可将其放在一条if语句中,如下所示:
# hello4.py
def hello():
print("Hello, world!")
def test():
hello()
if __name__ == '__main__': test()
如果将这个模块作为程序运行,将执行函数hello;如果导入它,其行为将像普通模块一样。
>>> import hello4
>>> hello4.hello()
Hello, world!
如你所见,我将测试代码放在了函数test中。原本可以将这些代码直接放在if语句中,但通过将其放在一个独立的测试函数中,可在程序中导入模块并对其进行测试。
>>> hello4.test()
Hello, world!
10.2 探索模块
10.2.1 模块包含什么
1、使用dir
要查明模块包含哪些东西,可使用函数dir,它列出对象的所有属性(对于模块,它列出所
有的函数、类、变量等)。如果将dir(copy)的结果打印出来,将是一个很长的名称列表(请试试看)。
>>> [n for n in dir(copy) if not n.startswith('_')]
['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']
2、变量all
在前一节中,我使用简单的列表推导来猜测可在模块copy中看到哪些内容,然而可直接咨询这个模块来获得正确的答案。你可能注意到了,在dir(copy)返回的完整清单中,包含名称all。这个变量包含一个列表,它与前面使用列表推导创建的列表类似,但是在模块内部设置的。下面来看看这个列表包含的内容:
>>> copy.__all__
['Error', 'copy', 'deepcopy']
10.2.2 使用help获取帮助
有一个标准函数可提供你通常需要的所有信息,它就是help。
10.2.3 文档
>>> 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).
这样就获得了函数range的准确描述。另外,由于通常是在编程时想了解函数的功能,而此时Python解释器很可能正在运行,因此获取这些信息只需几秒钟。
10.2.3 使用源代码
实际阅读源代码应该不成问题,但源代码在哪里呢?假设你要阅读标准模块copy的代码,可以在什么地方找到呢?一种办法是像解释器那样通过sys.path来查找,但更快捷的方式是查看模块的特性file。
>>> print(copy.__file__)
C:\Python35\lib\copy.py
网友评论