之前写过一篇博客:从几个实验来分析python import,但这篇博客过于简陋,而且在工作中也发现,很多人都对此处知识存在很多盲区,所以这里想去写一篇比较专业的。
从哪开始找
当你import一个模块时,将通过以下路径,对模块进行搜索。
- 在当前目录下搜索该模块
- 在环境变量PYTHONPATH中指定的路径列表中依次搜索
- 在python的安装路径中搜索
如果你想看一下你python的包搜索路径,不妨使用如下命令:
1 | import sys |
那么,如果你想把某个路径添加到python的搜索路径怎么办呢?
1 | PYTHONPATH=$PYTHONPATH:/xxx/xxx |
注: /xxx/xxx
就是你的路径。
package
module
在python中,一个包(package)其实就是一个目录,它里面可以有一个module,或者另一个包。
还有一个关键的东西,就是__init__.py
,此文件的目的就是用来告诉python解释器将该目录当成一个内容包,即该目录是一个包,里面包含了python模块。
那么在python解释器执行的时候,会将模块一个一个的导入进来,放到sys.modules中,这是一个字典。
你可以打印sys.modules
来一看究竟
从module到当前local
我先告诉你一件事情,你别看你在你的代码中写了import sys
,但其实sys早就导入了。
我们的代码在import sys
的时候,实际上不会重复再加载一次sys,而是将sys模块的名字引入到当前命名空间。
from future import absolute_import
如果你阅读过python源码,那么你可能经常会看见作者在开头写了这样一句话:from __future__ import absolute_import
字面意思感觉像是import此模块,就可以用“绝对的方式”进行导入了。但其实这就像tornado的@tornado.web.asynchronous
命名一样,坑了不少人。
此句import的真实目的是禁用 implicit relative import
, 但并不会禁掉 explicit relative import
那么它们有什么区别呢,首先我想告诉你,什么才是完整的包?
比如下面就是一个完整的包:
1 | things |
那么如果你在stool中引用bench,有如下几种方式:
1 | import bench # 此为implicit relative import |
实际上只有第三种,才是官方推荐的,第一种是官方强烈不推荐的,python3中已经不可以使用了,我们的from future import absolute_import也就是为了禁用这种方式。
当然有些读者觉得,我即使加上这句话,还是可以导入,那是因为,我们讨论都是基于包内的,那么什么是包,首先,包内之间需要引用,而对于主控,应该是包外部的,由它来引用包。
所以你直接来两个文件,然后在a.py中直接import b
来引用b.py,然后执行a.py没问题。这完全没有在我们的讨论的问题之内,所谓完整的包,包内和包外是有一个边界的。
比如你的main.py就是你的主控,它在你的main在things下,那么这个包并不能被成为完整的包。
两个错误
沿用上一节提到的包目录
1 | things |
我们在stool.py中编写:
1 | from .. books import horror |
然后执行stool.py文件,就会报错:
1 | Traceback (most recent call last): |
但是假如你把main放到things目录下,那么就会包这种错:
1 | Traceback (most recent call last): |
实际上就是说,如果你的主控在包中,那么你的包内相对引用就会出现问题,就像我在上一小节描述的“完整的包”
name
首先你在每个模块下都试着打印__name__
,而如果你在包内使用相对引用, 你试着将主控放到不同的地方去引用这些包,你会发现,它们打印出来的结果是不一样的。
这是因为__name__
来决定它在包结构中的位置
reloads
比如你现在在一个解释器中,你对一个源码进行修改,然后你想重新加载这个模块,这时候,你就需要reload。
reload实际上会擦出底层字典的内容,并通过重新执行模块的源代码来刷新它.
setdefaultencoding为什么要reloads?
在python2.x中,默认编码是ascii的,这也是人们在编写转码类代码时,会报错的原因:UnicodeDecodeError: 'ascii' codec can't decode byte ......
然后你就需要将这三行代码引进来。
1 | import sys |
虽然我们知道reload的作用,但是为什么要reload
?因为在python2.5以后,sys初始化之后,会删除sys.setdefaulting方法。