1、模块

文件的存储位置也很重要,将在下一节详细介绍。这里假设这个文件存储在目录C:\python
(Windows)或~/python(UNIX/macOS)中。
要告诉解释器去哪里查找这个模块,可执行如下命令(以Windows目录为例):

>>> import sys 
>>> sys.path.append('C:/python')

鉴于定义只需做一次,因此导入模块多次和导入一次的效果相同。

如果一定要重新加载模块,可使用模块importlib中的函数reload,它接受一个参数(要
重新加载的模块),并返回重新加载的模块。如果在程序运行时修改了模块,并希望这种修改
反映到程序中,这将很有用。要重新加载前述简单的模块hello(它只包含一条print语句),
可像下面这样做:

>>> import importlib 
>>> hello = importlib.reload(hello)
Hello, world!

这里假设hello已导入(一次)。通过将函数reload的结果赋给hello,用重新加载的版本
替换了以前的版本。由于打印出了问候语,说明这里确实导入了这个模块。
通过实例化模块bar中的类Foo创建对象x后,如果重新加载模块bar,并不会重新创建x指
向的对象,即x依然是(来自旧版bar的)旧版Foo的对象。要让x指向基于重新加载的模块中的
Foo创建的对象,需要重新创建它。

2、模块是用来下定义的

  1. 在模块中定义函数
  2. python 开箱即用_python

  3. 2.在模块中添加测试代码
# hello3.py 
def hello():
print("Hello, world!")
# 一个测试:
hello()

这看似合理:如果将这个模块作为普通程序运行,将发现它运行正常。然而,如果在另一个
程序中将其作为模块导入,以便能够使用函数hello,也将执行测试代码,就像本章的第一个hello
模块一样。

>>> import hello3 
Hello, world!
>>> hello3.hello()
Hello, world!
这不是你想要的结果。要避免这种行为,关键是检查模块是作为程序运行还是被导入另一个
程序。为此,需要使用变量__name__。
>>> __name__
'__main__'
>>> hello3.__name__
'hello3'

一个包含有条件地执行的测试代码的模块

def hello():
print("Hello world")

if __name__=="hello":hello()

python 开箱即用_python_02

3、让模块可用

修改了sys.path。sys.path包含一个目录(表示为字符串)列表,解释器将在这些目录中查找模块

  1. 将模块放在正确的位置
    目录site-packages是最佳的选择,因为它就是用来放置模块的。请在你的计算机中查看sys.path,找到目录site-packages,并将代码清单所示的模块保存到这里
  2. 告诉解释器到哪里去查找
    2.1办法之一是直接修改sys.path,但这种做法不常见。标准做法是将
    办法之二是模块所在的目录包含在环境变量PYTHONPATH中。

4、包

为组织模块,可将其编组为包(package)。包其实就是另一种模块,但有趣的是它们可包含其他模块。模块存储在扩展名为.py的文件中,而包则是一个目录。要被Python视为包,目录必须包含文件_init_.py。如果像普通模块一样导入包,文件_init_.py的内容就将是包的内容。例如,如果
有一个名为constants的包,而文件constants/_init_.py包含语句PI = 3.14,就可以像下面这样做:

import constants 
print(constants.PI)

要将模块加入包中,只需将模块文件放在包目录中即可。你还可以在包中嵌套其他包。
例如,要创建一个名为drawing的包,其中包含模块shapes和colors,需要创建如表10-1所示的文件和目
录(UNIX路径名)。

一种简单的包布局
文件/目录 描 述

~/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)

执行第1条语句后,便可使用目录drawing中文件__init__.py的内容,但不能使用模块shapes
和colors的内容。执行第2条语句后,便可使用模块colors,但只能通过全限定名drawing.colors来使用。执行第3条语句后,便可使用简化名(即shapes)来使用模块shapes。请注意,这些语句只是示例,并不用像这里做的那样,先导入包再导入其中的模块。换而言之,完全可以只使用第2条语句,第3条语句亦如此。

5、相关

>>>

没有引发异常,说明确实有这样的模块。但这个模块是做什么用的呢?它都包含些什么呢?
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']

结果包含dir(copy)返回的不以下划线打头的名称,这比完整清单要好懂些。
2. 变量__all__
在前一节中,我使用简单的列表推导来猜测可在模块copy中看到哪些内容,然而可直接咨询
这个模块来获得正确的答案。你可能注意到了,在dir(copy)返回的完整清单中,包含名称__all__。
这个变量包含一个列表,它与前面使用列表推导创建的列表类似,但是在模块内部设置的。下面
来看看这个列表包含的内容:

>>> copy.__all__ 
['Error', 'copy', 'deepcopy']

前面的猜测不算太离谱,只是多了几个并非供用户使用的名称。这个__all__列表是怎么来
的呢?为何要提供它?第一个问题很容易回答:它是在模块copy中像下面这样设置的(这些代码
是直接从copy.py复制而来的):

__all__ = ["Error", "copy", "deepcopy"]

为何要提供它呢?旨在定义模块的公有接口。具体地说,它告诉解释器从这个模块导入所有
的名称意味着什么。因此,如果你使用如下代码:

from copy import *

将只能得到变量__all__中列出的4个函数。要导入PyStringMap,必须显式地:导入copy并使用
copy.PyStringMap;或者使用from copy import PyStringMap。
编写模块时,像这样设置__all__也很有用。因为模块可能包含大量其他程序不需要的变量、函数和类,比较周全的做法是将它们过滤掉。如果不设置__all__,则会在以import *方式导入时,导入所有不以下划线打头的全局名称。

6 帮助

文档:method._doc_
模块:help(method)
使用源代码:_file_
斜体样式