import 的作用
import基本上是python最基本的元素之一,只要你需要协助,几乎就得import一个模块来达到目的,一句话概括import的作用就是:通过import,你可以获得使用其他模块的代码的权限。平时基本上只是使用,但是import其实是一个系统,里面的学问大着呢
import 的机制
import的语法,根据官网的文档,如下
import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*
| "from" relative_module "import" identifier ["as" name]
( "," identifier ["as" name] )*
| "from" relative_module "import" "(" identifier ["as" name]
( "," identifier ["as" name] )* [","] ")"
| "from" module "import" "*"
module ::= (identifier ".")* identifier
relative_module ::= "."* module | "."+
name ::= identifier
上面的说明类似正则表达式,只是体现了语法的格式,并没有解释module和relative_module是啥,我们暂时先忽略这里的含义,根据上面的说明,以下语法都是合法的:
- import module_a
这里的module_a是啥呢,module_a可以是一个文件夹,即使这个文件夹是空的,或者是一个py文件,即使文件是空的,如果是其他文件呢,假设有个文件为module_c.txt,那我们import module_c会发生什么呢,首先我们先在cmd命令行窗口 cd 到该文件所在的文件夹,然后输入python(前提是要安装python),在python命令行中输入import module_c,结果如下
>>> import module_c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named 'module_c'
可见import xxx,这个xxx,这个xxx称为模块(module),这个module包含什么呢,按照官方的文档介绍:A Python module which can contain submodules or recursively, subpackages,意思大概是一个python模块可以包含子模块也可以包含子包,子模块可以包含子子模块,以此类推。所以xxx必须是文件夹或者py后缀的文件,如果是文件夹,我们称这个为包,package,按照官方的文档,package分为两种,一种是regular package,另一种是namespace package。
1) regular package
正规的包包含有 __init__.py
文件
2)namespace package
命名空间包只包含子包,没有__init__.py
文件
两者有什么区别呢,下面我们分别创造这两种包,看一下import之后,他们有什么不一样,首先是regular package,文件结构如
– regular_package
|—-__init__.py
cmd下cd 到regular_package的上一级目录,打开python,执行以下操作
>>> import regular_package
>>> import sys
>>> sys.modules['regular_package']
<module 'regular_package'
from'F:\\yjm\\py_learn\\import_ex\\regular_package\\__init__.py'>
>>>
由此可见import导入的是regular_package下的__init__.py,并标明路径,下面请看namespace package,创建一个名为namespace_package的空文件夹,然后做如下操作 >>> import namespace_package
>>> import sys
>>> sys.modules['namespace_package']
<module 'namespace_package' (namespace)>
>>>
由此可见,没有__init__.py文件导入的包是一个namespace
模块和包有什么不一样呢,模块没有__path__属性,而包有__path__属性,__path__属性可以查看包的路径
- import module_a as ma
这个和import module_a的区别是将ma与module_a绑定起来,使用的时候使用ma,请看下面>>> import module_a as ma
>>> sys.modules['module_a']
<module 'module_a' (namespace)>
>>> sys.modules['ma']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'ma'
>>>
可见,存储到sys.modules中的模块名是原名并非别名 - import module_a.module_b vs from module_a import module_b
import module_a.module_b如下>>> import sys
>>> import module_a.module_b
>>> sys.modules['module_b']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'module_b'
>>> sys.modules['module_a.module_b']
<module 'module_a.module_b' (namespace)>
>>> sys.modules['module_a']
<module 'module_a' (namespace)>
>>>
from module_a import module_b如下>>> import sys
>>> from module_a import module_b
>>> sys.modules['module_b']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'module_b'
>>> sys.modules['module_a.module_b']
<module 'module_a.module_b' (namespace)>
>>> sys.modules['module_a']
<module 'module_a' (namespace)>
>>>
由上可见,import module_a.module_b和from module_a import module_b效果是一样的,import子模块的时候,会把父模块import进来,那到底是谁先import呢,下面来做个示例,创建如下文件结构
–module_a
|—__init__.py (里面写上print(‘module_a’)
)
|—module_b
|–|—__init__.py(里面写上print(‘module_b’)
)
- 然后
>>> import module_a.module_b
module_a
module_b
可知父模块会先被import - from module_a import *
这个是import module_a,不会import module_a的子模块,这样使用时不需要带上命名空间,如果有多个模块都采取这种import做法,如果有模块的变量名一样的话,会有覆盖的问题,创建如下文件结构
–module_x
|—__init__.py (内容如下)
’ (from .module1 import *
’ from .module2 import *
’ print(‘a = ‘, a))
|—module1.py (内容 a = 2)
|—module2.py (内容 a = 3)
输入>>> import module_x
a = 3
a = 3说明module1的a被覆盖了
假设是函数会怎样呢
–module_x
|—__init__.py (内容如下)
’ (from .module1 import *
’ from .module2 import *
’ func()
|—module1.py (内容如下)
’ (def func():
’ print(‘module1’))
|—module2.py (内容如下)
’ (def func():
’ print(‘module2’))
输入>>> import module_x
module2
可见函数也会被覆盖,若函数名一样,函数参数不一样呢
–module_x
|—__init__.py (内容如下)
’ (from .module1 import *
’ from .module2 import *
’ func()
’ func(1)
|—module1.py (内容如下)
’ (def func():
’ print(‘module1’))
|—module2.py (内容如下)
’ (def func(i):
’ print(‘module2’))
输入>>> import module_x
看来即使函数名一样,函数参数不一样的情况下,函数也会被覆盖,这个应该是python不支持重载导致的
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "F:\yjm\py_learn\import_ex\module_x\__init__.py", line 3, in <module>
func()
TypeError: func() missing 1 required positional argument: 'i' - import 多次相同的module
假设module_c.py的代码是print(‘module_c’),当第一次import module_c的时候会打印module_c,但是以后再import module_c就不会打印了,即除了第一次import外,其余的import都不会执行文件,按照官方文档,import的时候回去sys.modules检查是否存在需要import的module的key,如果存在,则不需要import,但是如果把sys.modules中的module_c删除掉后,import module_c又会打印module_c,所以一个模块可以被import,也可以在import 之后删掉。
比如在一个py文件中import module_a,另外还有其他py文件import module_a,各个文件共享module_a的成员,每个py文件的module_a都不是独立的 - from .module import xxx
这个module前面有一个点,一个点代表当前目录,两个点代表上一级目录,以此类推,这个是相对当前的py文件来说的,如果直接在终端输入这类的import是错误的,这种import只适用于写在py代码文件中,注意import .module是错误的 - import流程
import module,
1)先从sys.modules查找是否存在该module,如果存在则import ok,否则
2)从sys.meta_path中的Finder或者Importer查找,如果查找不到则
3)再从module.path(import a.b,会从a.path中去找b)和sys.path中按顺序从路径表中查找该module,如果查找到则import,不会再往下查找,所以路径表中包含相同的module时,哪个路径靠前哪个就被import - import hook
import hook 分两种,一种是meta hook,另一种是path hook。可以通过import hook改变import 机制,下面做个简单的示例,在个module第一次import的时候打印module的path,下面是代码
import sys
class Watcher(object):
@classmethod
def find_module(self, name, path, target=None):
print('importing', name, path, target)
return None
sys.meta_path.insert(0, Watcher)
然后每次import module(只要这个module不在sys.modules中)都会打印module 的path,除了最顶层的module,其他的module都会打印path
- import 相关内容
import 语法
sys.modules:存储已加载的module
sys.path:module搜索路径表
sys.meta_path:module importer表
package.__path__:package的路径,可更改,用于寻找子模块的路径
importlib.import_module():加载模块
importlib.reload():重新加载模块
import hook:可以改变import module的获取途径,以及module 被import时增加额外的操作