python的import module机制

2020年1月3日 / 77次阅读 / Last Modified 2020年1月3日
语法

python的import语句,引入一个模块。啥是python的模块?一个.py文件就是一个模块。import module干了三件需要关注的事情。

1,引入模块及其所有的全局符号

import module会将模块及其所有的全局符号引入到当前的namespace中。一般情况下,定义的函数,类,全局变量都是全局符号,都会被引入(不管是否有下划线)。访问方式为module.name。

这也就是为什么,很多python程序的入口,放在 if __name__ == '__main__': 这个条件判断下面。因为在这个判断下面,import module时,判断条件不会成立,下面的代码不会被执行。只有python module.py直接执行的时候,__name__才等于__main__。

那么,import module时,__name__是什么呢?

我们创建一个mod.py模块,此模块内只有一行代码:

$ cat mod.py
print(__name__)

然后,我们在python解释器中试试import:

>>> import mod
mod

import module时,__name__值为module

2,直接执行全局范围内的代码

这不是一个正常python代码应该干的事情,但是import module机制确实可以实现这样的功能。这也说明,引入符号的过程,就是代码执行的过程。只是引入的符号,代码都被包起来了(函数内,类中),没有调用的地方,就不会被真正执行。

比如有一个test_import.py模块,里面的代码如下:

$ cat test_import.py

print('12345')
a = 5
b = 6
print(a**6)

在解释器中import这个模块:

>>> import test_import
12345
15625
>>> test_import.a
5
>>> test_import.b
6

两行print都被直接执行。

3,修改sys.modules

sys.modules是一个dict对象,里面保存了当前python解释器执行的代码中,import的所有模块。

python解释器启动后,sys.modules里面就已经有一些模块被引入了,我理解这是python解释器自己就需要用到的模块。

>>> import sys
>>> for k,v in sys.modules.items(): print(k)
...
sys
builtins
_frozen_importlib
_imp
_thread
_warnings
_weakref
zipimport
_frozen_importlib_external
_io
marshal
posix
encodings
codecs
_codecs
encodings.aliases
encodings.utf_8
_signal
__main__
encodings.latin_1
io
abc
_abc
site
os
stat
_stat
posixpath
genericpath
os.path
_collections_abc
_sitebuiltins
readline
atexit
rlcompleter

不过,当前的namespace不能访问这些模块。需要显示的import,比如上面的import sys,我们再import几个模块看看:

>>> import time
>>> import os
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'k', 'os', 'sys', 'time', 'v']

这次当前namespace就有了sys,time,os模块(os模块已经在sys.modules中存在)可以访问。并且sys.modules里面也记录了这些模块。重要的是:这些模块的内存地址是一样的!

>>> id(os)
140461074332392
>>> id(sys.modules['os'])
140461074332392
>>>
>>> os.listdir()
['__pycache__', 'nohup.out', 'info.txt', 'test.py', 'httpd-2.4.38.tar.gz', 'httpd-2.4.38', 'ct', 'test_import.py', 'mod.py', 'jian.h', 'test.o', 'jian.o', 'text.py', 'test.c', 'jian.c', 'a.out']
>>> sys.modules['os'].listdir()
['__pycache__', 'nohup.out', 'info.txt', 'test.py', 'httpd-2.4.38.tar.gz', 'httpd-2.4.38', 'ct', 'test_import.py', 'mod.py', 'jian.h', 'test.o', 'jian.o', 'text.py', 'test.c', 'jian.c', 'a.out']

如果import一个已经在sys.modules中存在的模块,只是将sys.modules记录的地址和名称copy到当前的namespace。

>>> def test():
...     import os
...     print(id(os))
...
>>> test()
140461074332392

python的namespace机制,是C语言所没有的。C语言链接之后,所有的全局符号都可以相互任意访问,只要知道地址。而在python中,访问能否成功,一定要关心namespace。

刚才说了sys.modules里面保存的是import的所有模块,这包括了模块中import的其它模块,其它模块import的更多的模块......我们看一个例子,我修改了test_import.py的代码,如下:

$ cat test_import.py


import time
import argparse
import datetime

$ python3
Python 3.7.1 (default, Oct 30 2018, 20:38:04)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> len(sys.modules.keys())
35
>>> import test_import
>>> len(sys.modules.keys())
63

在python解释器中,import test_import,导致sys.modules中的模块数量暴增!我这里就不打印明细了。

-- EOF --

本文链接:https://www.pynote.net/archives/1687

留言区

《python的import module机制》有1条留言

电子邮件地址不会被公开。 必填项已用*标注

  • 麦新杰

    模块或符号在不在内存中是一回事儿,能不能访问,要看它在不在当前的namespace中。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top