乐趣区

python基础教程模块高级技巧

上一节,我们讲解了 Python 模块的基础知识,这一节我们继续深入了解模块的更多知识,从而让大家全面了解、掌握和运用模块到我们实际的编程中。

在上一节中有一句话“接着我们在这个文件所在目录运行 Python 解释器 IPython”,不知道大家还记不记得。这就话背后隐含的意思是,在这个文件(模块)目录下,我们才可以 import 到这个模块,否则会报错,说找不到这个模块。这里,就涉及到了 Python 模块的搜索路径。

Python 模块的搜索路径

当一个名为 mylib 的模块被导入时,解释器首先搜索内置模块是不是有该名字的模块。
如果没有找到,接着搜索 sys.path 列出的目录下面是不是有名为 mylib 的模块。

sys.path的初始化按以下几个路径的顺序:
**(1)包含输入脚本的目录,如果没有输入脚本则是当前目录;
(2)环境变量 PYTHONPATH(一个目录名称的列表);
(3)Python 库的安装目录。**

下面我们来验证一下 sys.path 所包含的路径,编写一个 Python 文件 initpath.py 如下:

# Author: veelion
# file: initpath.py

import sys
print('\n'.join(sys.path))

命令行下运行:python3 initpath.py得到如下结果:

$ python mylib.py
/home/veelion/p2/tutorial/md_Python/codes
/home/veelion/.virtualenvs/py3.7/lib/python37.zip
/home/veelion/.virtualenvs/py3.7/lib/python3.7
/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload
/usr/lib/python3.7
/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages

我们可以发现,initpath.py所在的目录是 sys.path 列表的第一个元素。符合上面三条原则的顺序。

接下来我们通过交互式 Python 解释器来看看sys.path,运行 CPython 解释器再导入 sys:

>>> import sys
>>> sys.path
['','/home/veelion/.virtualenvs/py3.7/lib/python37.zip','/home/veelion/.virtualenvs/py3.7/lib/python3.7','/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload','/usr/lib/python3.7','/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages']

细心的小猿可以发现,sys.path的第一项是个 空字符串,和运行脚本方式下的第一项——当前路径不一样。为什么会是空字符串呢?

这是因为,当交互式运行 Python 解释器时(或者脚本是从标注输入读取的),可以认为传给解释器的脚本文件路径为空,那么就把 sys.path[0] 设置为空字符串,它告诉 Python 搜索模块时先从当前文件夹开始。

以上两种方法验证的 sys.path 都符合预期,然而 IPython 有点例外。

In [1]: import sys

In [2]: sys.path
Out[2]: 
['/home/veelion/.virtualenvs/py3.7/bin',
 '/home/veelion/.virtualenvs/py3.7/lib/python37.zip',
 '/home/veelion/.virtualenvs/py3.7/lib/python3.7',
 '/home/veelion/.virtualenvs/py3.7/lib/python3.7/lib-dynload',
 '/usr/lib/python3.7',
 '','/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages','/home/veelion/.virtualenvs/py3.7/lib/python3.7/site-packages/IPython/extensions','/home/veelion/.ipython']

IPython 并没有把当前路径放在第一项。这样就会导致你写的模块与系统模块重名时,它 import 的是系统模块而不是你写的模块,而前面两种方式就是导入你写的模块而非系统模块。这一点在使用 IPython 时要格外注意。

注意 你可以在程序中修改 sys.pathsys.path 是一个 Python 的列表结构,我们可以像修改列表那样修改它,增加、删除、修改路径顺序。比如,可以通过 sys.path.insert(0, 'my-module-path') 来把我们自己写的模块的路径放到搜索路径的最前面,优先搜索自己的模块。

编译后的 Python 文件:*pyc

为了加速模块的加载时间,Python 会缓存已经编译好的模块,并把它们放在与模块同级目录下的 __pycache__ 文件夹下面,编译好的模块的命名方式为:module.version.pyc,其中的 version 包含 Python 的版本号。比如:

$ ls __pycache__/
m1.cpython-36.pyc  m2.cpython-36.pyc

cpython-36就是编译这个模块的 Python 信息:用 CPython 3.6 进行编译的。这种命名方式方便不同版本的 Python 编译的模块同时存在而不造成冲突。

Python 在两种情况下不检查缓存。
其一,它总是重新编译并且不存储直接从命令行加载的模块的结果。
其二,如果没有模块源码文件,它不会检查缓存。要支持非源(仅编译)分发,已编译的模块必须位于源码目录中,并且不得有模块源码。

举个例子理解一下这两点:
(1)如果在命令行下运行 python m1.py,Python 总是从新编译m1.py,但不会保存 pyc 文件,因为每次都有重新编译就没必要保存了。
(2)如果我们导入m1 模块时,搜索路径目录下只有 m1.pyc 而没有 m1.py 文件,那就直接导入 m1.pyc。这种方式适合把编译好的 pyc 发布给其他人而不是给它们源代码,使用这种方式时,把.pyc 文件从 __pycache__ 中拷贝到 .py 文件相同的目录下并删掉 .py 文件即可。

Python 模块的高级技巧

(1)模块 compileall 可以把一个文件夹下所有的 py 文件编译成.pyc 文件。
它的使用很简单,命令行运行的格式如下:
python -m compileall 文件夹或文件名
更多选项可以通过:python -m compileall -h查看。

(2)编译成 .pyc 文件时,可以给 Python 命令两个选项:-O-OO,使得编译后的文件更小。
-O 去除 assert 语句;
-OO 去除 assert 语句和__doc__ string
根据情况来使用这两个选项,用 compileall 编译文件时加这个选项就是这样子的:
python -O -m compileall 文件夹或文件名

生成的 pyc 文件名称里面有 opt- 标签,-O的标签是 opt--OO 的标签就是opt-2。比如:

$ ls -F -1 __pycache__/
m1.cpython-36.opt-1.pyc
m1.cpython-36.opt-2.pyc
m1.cpython-36.pyc

(3).pyc.py 文件都不会使程序运行得更快(不会提高运行速度)。但是,.pyc文件能使加载速度更快,因为少了编译的过程。

Python 标准模块

Python 附带了一个标准模块库。其中一些模块内置在解释器中,它们提供对不属于语言核心但仍然内置的操作的访问,以提高效率或提供对系统调用等操作系统原语的访问。这些模块的集合是一个配置选项,它也取决于底层平台。例如,winreg模块仅在 Windows 系统上提供。一个值得注意的模块是sys,它内置于每个 Python 解释器中。

Python 的标注模块会在我们今后的编程中不断遇到和使用,具体的学习可以在今后用到时再学习。你需要记住的一点是:当你写 Python 代码需要某些基本功能时,一定要先找找看是否已经有标准模块存在,是否已经有人写好了包含这些功能的模块,最后才要觉得自己要不要实现这些功能。

Python 内置函数 dir()

dir()用来查看模块里面定义的名字,包括:变量名,模块名,函数名等等。
它返回一个 list:

In [5]: import my_first_module

In [6]: dir(my_first_module)
Out[6]: 
['MY_NAME',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'my_add',
 'my_print']

如果调用 dir()不传递参数,则列出当前已经定义的所有名字:

In [1]: import my_first_module

In [2]: dir()
Out[2]: 
['In',
 'Out',
 '_',
 '__',
 '___',
 '__builtin__',
 '__builtins__',
 '__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_dh',
 '_i',
 '_i1',
 '_i2',
 '_ih',
 '_ii',
 '_iii',
 '_oh',
 'exit',
 'get_ipython',
 'my_first_module',
 'quit']

用 dir()可以查看所有的内置类型、变量、函数等,方法是借助标准模块builtins

>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

Python 模块高级技巧总结

(1)Python 的搜索路径,顺序一定要搞得清;
(2)编译后的 .pyc 文件;
(3)dir()内置函数查看模块定义的名字。

退出移动版