Python的import机制

package 和 module 概念

  1. module 可以是一个py文件,一个pyd文件,dll文件,一个so文件。
  2. package必须是一个文件夹,并且文件夹里面必须有__init__.py文件。
  3. import 文件夹A,实际上是把<module 'A' from 'D:\python Code\A\__init__.py'>动态加载到sys.modules。

关于import

当你执行一个import操作

  1. Python虚拟机把module A动态加载到sys.modules
  2. Python虚拟机引入符号'A',
  3. 把'A'映射到sys.modules的module A
  4. 把'A'加入到当前名字空间
>>> import A
>>> dir()
['A','__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__' ]

但是,import A.test,为什么当前名字空间没有A.test?

  1. Python虚拟机把module A动态加载到sys.modules
  2. 在module A的path搜索module test
  3. Python虚拟机把module A.test动态加载到sys.modules
  4. Python虚拟机引入符号'A',
  5. 把'A'映射到sys.modules的module A
  6. 把'A'加入到当前名字空间
>>> import A.test
>>> dir()
['A', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']

为什么是A.test?这是为了让A.test和B.test在sys.modules里面和平共存。

当你执行一个import as操作

  1. Python虚拟机把A.test动态加载到sys.modules
  2. Python虚拟机引入符号't',
  3. 把't'映射到sys.modules的module A.test
  4. 把't'加入到当前名字空间
>>> import A.test as t
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 't']

所有的import的模块都会在sys.module缓存。

举个例子:
test3.py

B = 4

del test3,不能调用test3.B。但是sys.module仍然存在test3。

import sys
import test3

print(sys.modules['test3'])
print(test3.B)
del test3
print(sys.modules['test3'])
print(test3.B)

输出:

<module 'test3' from 'D:\\python Code\\test3.py'>
4
<module 'test3' from 'D:\\python Code\\test3.py'>
NameError: name 'test3' is not defined

from import 和 import

举个例子:
A/test.py

B = 3

命令行执行:

>>> from A.test import B
>>> dir()
['B', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> import sys
>>> print(sys.modules['A'])
<module 'A' from 'D:\\python Code\\A\\__init__.py'>
>>> print(sys.modules['A.test'])
<module 'A.test' from 'D:\\python Code\\A\\test.py'>

from import 和import没有本质区别,都是要把A, A.test 加载到sys.modules。
区别是from import 会把符号'B'加入到当前名字空间,但没有把符号'A'加入到当前名字空间。

关于reload

importlib.reload 只会更新模块修改了的对象和新增的对象。
源码文件删除了的对象,reload后依然存在。

  1. test.py文件内容
class A():
     pass
  1. 命令行执行:
>>>import test
>>>print(test.A)
<class 'test.A'>
  1. test.py修改后
B = 1
  1. 命令行执行:
>>>import importlib
>>>importlib.reload(test)
>>>test.B
1
>>>test.A
<class 'test.A'>

关于import和reload

所有的import动作,第一次都会在sys.module缓存。
第二次之后的import都是使用sys.module缓存。
reload相当于重新执行一次import。
举个例子:
test1.py

import test3

class A():
    pass

test2.py

import test3
print('first:', test3.B)
test3.B = 1
print('change:', test3.B)
import test1
# from test1 import A
print('import test1:',test3.B)
import importlib
importlib.reload(test3)
print('reload:', test3.B)

test3.py

B = 4
print('hello')

执行test2.py的结果

hello
first: 4
change: 1
import test1: 1
hello
reload: 4

避免全局变量在reload中被替换

把test3.py改成如下,即可避免B在reload的时候还原。

if 'B' not in globals():
    B = 4
print('hello')

循环import的问题与解决方案

举个例子:
test1.py

import test2

class A():
    def __init__(self):
        test2.test()

a = A()

test2.py

import test1

def test():
    print(test1.A)

如果执行test1.py,运行顺序如下报错所示:

Traceback (most recent call last):
  File "D:/python Code/test1.py", line 2, in <module>
    import test2
  File "D:\python Code\test2.py", line 2, in <module>
    import test1
  File "D:\python Code\test1.py", line 8, in <module>
    a = A()
  File "D:\python Code\test1.py", line 6, in __init__
    test2.test()
AttributeError: module 'test2' has no attribute 'test'

因为test2.py里面import了test1.py,所以又继续test1.py的初始化。
执行到a = A()的时候,test2.py还没初始化完,所以没有test函数。

解决方案:

  1. 如果是脚本执行,在test1.py改为如下即可:
if __name__ == '__main__':
    a = A()

__name__是python的一个内置类属性,它存储模块的名称。
python的模块既可以被调用,也可以独立运行。而被调用时__name__存储的是模块名(test1),独立运行时存储的是__main__。它的作用主要就是用来区分,当前模块是独立运行还是被调用。

  1. 如果test1是被作为模块调用的场合,把test2.py改为如下即可:
def test():
    import test1
    print(test1.A)

test2在需要的时候才import test1,这时候可以确保test1已经初始化完成。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 模块和包 一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是...
    go以恒阅读 2,333评论 0 4
  • If you quit from the Python interpreter and enter it agai...
    linyk3阅读 449评论 0 0
  • 初恋就像是林肯法球,投入大量时间和金钱,结果只能避免自己再次受伤。 分手就像是疯狂面具,顺风时,对对方造成更大伤害...
    赵乐子丨努力奋斗阅读 417评论 1 4
  • 这里新人空微 2000年老阿姨,在上海大学外国语学院上大一(欢迎来偶遇鸭) 喜欢更的文嘛,一般都是甜文(笑)不喜虐...
    空微阅读 133评论 0 1
  • 人到了一定高度就要选择抛弃,而不是装不下的欲望。年轻是资本,可以春花秋月,可以风韵昂扬,当岁月撕碎容颜的时候,便是...
    d3a17469fa4f阅读 136评论 0 2

友情链接更多精彩内容