模块就是程序,任何Python程序都可作为模块导入。
第一步:编写程序,示例如下:
#hello.py
print('hello world!')
第二步:告诉解释器去哪里查找这个模块
假设文件存储在了path = '/home/dulingwen/site-packages'中,可使用如下命令:
>>>import sys
>>>sys.path.append('/home/dulingwen/site-packages')
这告诉解释器,除了查找默认的位置外,还可以到目录/home/dulingwen/site-packages中去查找这个模块。这样做后,就可以导入这个模块了。
注意:当你导入模块时,会发现其所在目录中除源代码文件外,还新建了一个名为 __pycache__的子目录。这个目录包含处理后的文件,Python能够更高效地处理它们。以后再导入这个模块时,如果.py文件未发生变化,Python将导入处理后的文件,否则将重新生成处理后的文件。删除目录 __pycache__ 不会有任何害处,因为必要时会重新创建它。
示例如下:
#hello2.py
#定义函数
def hello():
print('hello world!')
#测试代码
hello()
此时,导入模块你会发现,自动执行了测试代码hello()函数:
>>>import hello2
hello world!
>>>hello2.hello()
hello world!
在主程序中(包括解释器的交互式提示符),变量 __name__ 的值是 '__main__' ,而在导入的模块中,这个变量被设置为该模块的名称,因此可以使用变量 __name__检查模块是作为程序运行还是被导入另一个程序。示例代码如下:
# hello3.py
def hello():
print("Hello, world!")
def test():
hello()
if __name__ == '__main__':
test()
此时如果导入模块:
>>>import hello3
>>>hello3.hello()
hello world!
>>>hello3.test()
hello world!
这里共有三种方法:
将模块放在正确的位置很容易,只需找出Python解释器到哪里去查找模块,再将文件放在这个地方即可。使用下列代码可以查看:
import sys,pprint
pprint.pprint(sys.path)
输出结果如下:
['/data/ananconda3/envs/tensorflow/lib/python36.zip',
'/data/ananconda3/envs/tensorflow/lib/python3.6',
'/data/ananconda3/envs/tensorflow/lib/python3.6/lib-dynload',
'/data/ananconda3/envs/tensorflow/lib/python3.6/site-packages',
'/data/tensorflow/models/research',
'/data/tensorflow/models/research/slim',]
(提示:如果要打印的数据结构太大,一行容纳不下,可使用模块 pprint 中的函数 pprint (而不是普通 print 语句)。 pprint 是个卓越的打印函数,能够更妥善地打印输出。)
只要模块位于类似于site-packages这样的地方,所有的程序就都能够导入它。
将模块放在正确的位置可能不是合适的解决方案,原因很多,比如:不希望Python解释器的目录中充斥着你编写的模块;没有必要的权限,无法将文件保存到Python解释器的目录中;想将模块放在其他地方。
方法一:使用sys.path.append()。
方法二:标准方法:将模块所在的目录包含在环境变量 PYTHONPATH 中,环境变量 PYTHONPATH 的内容随操作系统而异(但它基本上类似于sys.path,也是一个目录列表)。示例如下,可以将~/python 附加到环境变量 PYTHONPATH 末尾:
export PYTHONPATH=$PYTHONPATH:~/python
如果要对所有启动的shell都执行这个命令,可将其添加到主目录中的.bashrc文件中。也可以采用下列方式设置环境变量。
设置永久环境变量并立即生效:
系统环境变量可存储在以下文件中:
/etc/profile
/etc/profile.d(它是文件夹)
/etc/bash.bashrc
首先设置永久环境变量实例(以/etc/profile为例):
gedit /etc/profile
#或者
vim /etc/profile
在文件末尾处添加如下,保存并退出:
export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.7/dist-packages/gdal.py
方法三:使用路径配置文件,这些文件的扩展名为.pth,位于一些特殊目录中,包含要添加到 sys.path 中的目录。示例如下:
#from /data/ananconda3/envs/tensorflow/lib/python3.6/site-packages
touch tf_odi.pth
gedit tf_odi.pth
#在tf_odi.pth文件中手动添加如下路經
home/dulingwen/tensorflow/models/research
为组织模块,可将其编组为包(package)。包其实就是另一种模块,但有趣的是它们可包含其他模块。模块存储在扩展名为.py的文件中,而包则是一个目录。要被Python视为包,目录必须包含文件__init__.py。要将模块加入包中,只需将模块文件放在包目录中即可。你还可以在包中嵌套其他包。一种简单的包布局如下:
文件/目录 | 描述 |
~/python/ ~/python/drawing/ ~/python/drawing/__init__.py ~/python/drawing/colors.py ~/python/drawing/shapes.py |
PYTHONPATH 中的目录 包目录(包 drawing ) 包代码(模块 drawing ) 模块 colors 模块 shapes |
1.使用 dir
要查明模块包含哪些东西,可使用函数 dir ,它列出对象的所有属性(对于模块,它列出所有的函数、类、变量等),如果将 dir(module) 的结果打印出来,将是一个很长的名称列表(请试试看)。在这些名称中,有几个以下划线打头。根据约定,这意味着它们并非供外部使用。有鉴于此,我们使用一个简单的列表推导将这些名称过滤掉。
>>> import copy
>>> dir(copy)
['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']
>>> [n for n in dir(copy) if not n.startswith('_')]
['Error', 'copy', 'deepcopy', 'dispatch_table', 'error']
#结果包含 dir(copy) 返回的不以下划线打头的名称,这比完整清单要好懂些。
2.变量 “__all__”
>>> copy.__all__
['Error', 'copy', 'deepcopy']
(1)变量 “__all__”怎么来的?
它是在模块 copy 中像下面这样设置的(这些代码是直接从copy.py复制而来的):
__all__ = ["Error", "copy", "deepcopy"]
(2)为什么提供它?
旨在定义模块的公有接口。具体地说,它告诉解释器从这个模块导入所有的名称意味着什么。因此,如果你使用如下代码:
from copy import *
将只能得到变量 __all__ 中列出的4个函数。要导入 PyStringMap ,必须显式地:导入 copy 并使用copy.PyStringMap;或者使用 from copy import PyStringMap 。编写模块时,像这样设置 __all__ 也很有用。因为模块可能包含大量其他程序不需要的变量、
函数和类,比较周全的做法是将它们过滤掉。如果不设置 __all__ ,则会在以 import * 方式导入时,导入所有不以下划线打头的全局名称。
3.使用 help 获取帮助
>>> help(copy.copy)
Help on function copy in module copy:
copy(x)
Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
上述帮助信息指出,函数 copy 只接受一个参数 x,且执行的是浅复制。在帮助信息中,还提到了模块的 __doc__ 字符串。首先介绍一下文档字符串:文档字符串就是在函数开头编写的字符串,用于对函数进行说明。而函数的属性 __doc__ 可能包含这个字符串。
>>> print(copy.copy.__doc__)
Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
显然这种方法不如使用help看到的信息更多。
4.文档
文档是有关模块信息的自然来源。
>>> 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).
然而,并非每个模块和函数都有详尽的文档字符串(虽然应该如此),且有时需要有关工作原理的更详尽描述,因此提供以下几个参考文件:
Python库参考手册
Python 语言参考
5.使用源代码
多数情况下前面讨论的探索技巧都够用了。但要真正理解Python语言,可能需要了解一些不阅读源代码就无法了解的事情。事实上,要学习Python,阅读源代码是除动手编写代码外的最佳方式。那么源代码在哪里呢?这里提供一个查找源代码的快捷的方式:查看模块的特性“ __file__ ”。示例如下:
>>> print(copy.__file__)
/data/ananconda3/lib/python3.7/copy.py
找到了!你可在代码编辑器(如IDLE)中打开文件copy.py,并开始研究其工作原理。如果列出的文件名以.pyc结尾,可打开以.py结尾的相应文件。
警告:在文本编辑器中打开标准库文件时,存在不小心修改它的风险。这可能会破坏文件。因此关闭文件时,千万不要保存你可能对其所做的修改。
请注意,有些模块的源代码你完全无法读懂。它们可能是解释器的组成部分(如模块 sys ),还可能是使用C语言编写的(如果模块是使用C语言编写的,应该能够获取其C语言源代码。有关如何使用C语言扩展Python的详细信息,请参阅第17章)。