上一节,我们讲解了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.path。sys.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()内置函数查看模块定义的名字。
我的公众号:猿人学 Python 上会分享更多心得体会,敬请关注。
***版权申明:若没有特殊说明,文章皆是猿人学 yuanrenxue.com 原创,没有猿人学授权,请勿以任何形式转载。***