python import 迷宫
来源:互联网 发布:网络语35是什么意思 编辑:程序博客网 时间:2024/05/16 02:18
import 迷宫
问题
cleven <shenglipang@gmail.com>回覆至 python-cn@googlegroups.com收件人 python-cn@googlegroups.com日期 2008年11月25日 下午 12:01主旨 [CPyUG:72341] import嵌套的问题
看了《Python源码剖析》,里面提到的嵌套import的问题还是没有弄明白,各位给看一下吧。
[A.py]from B import Dclass C:pass[B.py]from A import Cclass D:pass
为什么执行A的时候不能加载D呢?
如果将A.py改为:import B就可以了。
这是怎么回事呢?
Robert Chen:详解
Robert Chen <search.pythoner@gmail.com>回覆至 python-cn@googlegroups.com收件人 python-cn@googlegroups.com日期 2008年11月25日 下午 1:41主旨 [CPyUG:72362] Re: import嵌套的问题
恩,这跟Python内部import的机制是有关的,具体到from B import D,Python内部会分成几个步骤:
- 在sys.modules中查找符号"B"
如果符号B存在,则获得符号B对应的module对象<module B>
从<module B>的__dict__中获得符号"D"对应的对象,如果"D"不存在,则抛出异常
如果符号B不存在,则创建一个新的module对象<module B>,注意,这时,module对象的__dict__为空
执行B.py中的表达式,填充<module B>的__dict__
从<module B>的__dict__中获得"D"对应的对象,如果"D"不存在,则抛出异常
所以,这个例子的执行顺序如下:
1、执行A.py中的from B import D 由于是执行的python A.py,所以在sys.modules中并没有<module B>存在, 首先为B.py创建一个module对象(<module B>), 注意,这时创建的这个module对象是空的,里边啥也没有, 在Python内部创建了这个module对象之后,就会解析执行B.py,其目的是填充<module B>这个dict。2、执行B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在<module A>了, 由于这时缓存还没有缓存<module A>, 所以类似的,Python内部会为A.py创建一个module对象(<module A>), 然后,同样地,执行A.py中的语句3、再次执行A.py中的from B import D 这时,由于在第1步时,创建的<module B>对象已经缓存在了sys.modules中, 所以直接就得到了<module B>, 但是,注意,从整个过程来看,我们知道,这时<module B>还是一个空的对象,里面啥也没有, 所以从这个module中获得符号"D"的操作就会抛出异常。 如果这里只是import B,由于"B"这个符号在sys.modules中已经存在,所以是不会抛出异常的。
ZQ:图解
JUDE绘制:PyImportFlow.jude
编译追踪
hiter的日记:
python的import的研究
问题代码如下:
A.pyfrom A import Bclass B(object):pass>>> import ATraceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/john/pythonstudy/mypython/bin/A.py", line 9, in <module> from A import BImportError: cannot import name B>>>
阅读代码后发现: 字节码大概
9 0 LOAD_CONST 0 (-1) 3 LOAD_CONST 1 (('B',)) 6 IMPORT_NAME 0 (A) 9 IMPORT_FROM 1 (B) 12 STORE_NAME 1 (B) 15 POP_TOP 10 16 LOAD_CONST 2 ('B') 19 LOAD_NAME 2 (object) 22 BUILD_TUPLE 1 25 LOAD_CONST 3 (<code object B at 0xb7a1fa88, file "A.py", line 10>) 28 MAKE_FUNCTION 0 31 CALL_FUNCTION 0 34 BUILD_CLASS 35 STORE_NAME 1 (B) 12 38 LOAD_CONST 4 ('hi') 41 PRINT_ITEM 42 PRINT_NEWLINE 43 LOAD_CONST 5 (None) 46 RETURN_VALUE
- 可以看出整个import的过程是:先import A,然后再import A然后报错。
经过分析发现原因是:在import A时,虚拟机发现sys.modules(在import_submodule中会做检查)中没有加载过A,然后新建了一个A的module,新建的module是空的,需要向里面加入__builtin__,__file__等属性(在执行下一个import的时候,新module的dict将作为globals(locals)传给执行(A)字节码时使用),然后虚拟机会将这个新的module加入sys.modules中,至此虚拟机的调用堆栈如下:(代码行号可能不对,因为源码中加入了很多调试输出代码)
#0 PyImport_AddModule (name=0xbfd91673 "A") at Python/import.c:617 <-------PyImport_AddModule 在这里#1 0x08106271 in PyImport_ExecCodeModuleEx (name=0xbfd91673 "A", co=0xb7da6748, pathname=0xbfd8f533 "A.pyc") at Python/import.c:653#2 0x08106c67 in load_source_module (name=0xbfd91673 "A", pathname=0xbfd8f533 "A.pyc", fp=0x821bd60) at Python/import.c:963#3 0x081085cf in load_module (name=0xbfd91673 "A", fp=0x821bd60, buf=0xbfd905d3 "A.py", type=1, loader=0x0) at Python/import.c:1753#4 0x0810a39b in import_submodule (mod=0x818c888, subname=0xbfd91673 "A", fullname=0xbfd91673 "A") at Python/import.c:2433 <--------import_submodule 在这里#5 0x081098bb in load_next (mod=0x818c888, altmod=0x818c888, p_name=0xbfd91654, buf=0xbfd91673 "A", p_buflen=0xbfd9166c) at Python/import.c:2234#6 0x08108e1c in import_module_level (name=0x0, globals=0xb7de82b4, locals=0xb7de82b4, fromlist=0x818c888, level=-1) at Python/import.c:2005#7 0x081093a1 in PyImport_ImportModuleLevel (name=0xb7de115c "A", globals=0xb7de82b4, locals=0xb7de82b4, fromlist=0x818c888, level=-1) at Python/import.c:2076#8 0x080d8809 in builtin___import__ (self=0x0, args=0xb7d9de34, kwds=0x0) at Python/bltinmodule.c:47#9 0x0814d04b in PyCFunction_Call (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Objects/methodobject.c:77#10 0x08062974 in PyObject_Call (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Objects/abstract.c:1861#11 0x080ecad2 in PyEval_CallObjectWithKeywords (func=0xb7dcf5ac, arg=0xb7d9de34, kw=0x0) at Python/ceval.c:3446#12 0x080e7b33 in PyEval_EvalFrameEx (f=0x821bc04, throwflag=0) at Python/ceval.c:2068#13 0x080eaf9e in PyEval_EvalCodeEx (co=0xb7d9ab08, globals=0xb7de82b4, locals=0xb7de82b4, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:2840#14 0x080e013e in PyEval_EvalCode (co=0xb7d9ab08, globals=0xb7de82b4, locals=0xb7de82b4) at Python/ceval.c:494#15 0x08116ab0 in run_mod (mod=0x8220378, filename=0x81653bb "<stdin>", globals=0xb7de82b4, locals=0xb7de82b4, flags=0xbfd92f70, arena=0x81c5cd8) at Python/pythonrun.c:1273#16 0x081151e1 in PyRun_InteractiveOneFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", flags=0xbfd92f70) at Python/pythonrun.c:792#17 0x08114e54 in PyRun_InteractiveLoopFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", flags=0xbfd92f70) at Python/pythonrun.c:723#18 0x08114cac in PyRun_AnyFileExFlags (fp=0xb7f4d440, filename=0x81653bb "<stdin>", closeit=0, flags=0xbfd92f70) at Python/pythonrun.c:692#19 0x08059d60 in Py_Main (argc=1, argv=0xbfd93074) at Modules/main.c:523#20 0x08058e26 in main (argc=136033156, argv=0xb7dc37b4) at ./Modules/python.c:23
可以新建module并将其加入sys.modules是在函数PyImport_ExecCodeModuleEx中完成的,此后,就会将新module的dict作为locals(globals)传给执行A字节码的函数,在执行A字节码时,发现需要IMPORT_NAME A,这时虚拟机会发现在sys.modules中已经存在A,所以会直接返回这个A的module,而在接下来的IMPORT_FROM时,会从这个module中试图找到B,而此时这个module里虚拟机只加载了__builtin__,__file__等属性,加载B的字节码还没执行到(也不可能执行到),所以虚拟机就会抛出无法加载B的异常。
在<python源码剖析>前言中提到这样一个问题:
[A.py]from B import Dclass C: pass[B.py]from A import Cclass D: pass
这里无法加载D,这个问题是和本文一开始提出的问题相似的。
总结:
- IMPORT_NAME字节码命令的执行流程如
- 假设需要import A,那么虚拟机首先在sys.modules中查找是否已经load过A,
- 找到则返回该对象,命令结束;
- 如果没有找到,那么虚拟机会新建一个module对象,
然后向module对象中添加必要的属性(builtin等),
- 然后用这个module中的dict作为globals(locals)执行A,
- 然后返回
反馈
- python import 迷宫
- python import、from import
- Python import
- Python import
- Python import
- Python import
- python import
- python import
- Python-import
- Python import
- Python import
- python:import
- python import
- python import
- Python Gossip: import、import as、from import
- python import 与from ... import
- 【Python】import class/import module
- Python import 与 from import
- python
- python复杂网络库networkx:算法
- C语言编译器
- Android系统中的进程管理
- Python 标准异常总结
- python import 迷宫
- select取数据库值设为默认值
- 对gif动图进行缩放等处理(java)
- 巧用Grep 命令
- Ionic 整理项目结构 - 抽离 + 功能模块划分
- JAVA Socket 编程学习笔记(一)
- php中 curl模拟post发送json并接收json
- mysql(5.7.17)解压版安装
- 图标适配