属性错误模块对象没有属性——循环导入引起的?
Attribute error module object has no attribute -- Caused by circular imports?
OTS 模块
from Tkinter import *
import openTableApiGet
#bunch of code blah blah blah
openTableApiGet.main() # call to the method in the OpenTableApiGet module
模块 OpenTableApiGet
import OTS
class Parser:
#Bunch of code in the class doing stuff
def main():
#bunch of code
#The main method the complier says this module
#doesn't have. Outside of `the Parser class,
#just hanging out on its own
为什么会这样?是循环导入的罪魁祸首我宁愿保留它,但如果我必须改变它,我会的。我需要写更多的东西来让 Whosebug 开心,所以我希望你能尽快在生活中找到炸玉米饼!
谢谢大家
您是对的,循环导入会使您显示的代码在某些情况下失败。具体来说,如果外部代码首先导入 OpenTableApiGet
模块,OTS
模块将在尝试调用 OpenTableApiGet.main()
.
时失败
原因如下。当 Python 加载模块时,它从顶部开始并且 运行 按顺序加载每个语句。对于导入语句,它可能必须暂停该模块的执行才能加载另一个模块。
这是一个例子:
A.py:
print 1
import B
print 3
B.py
print 2
这两个简单的会在你导入的时候按顺序打印数字1-3 A
。
导入语句并不总是暂停。如果要导入的模块已经加载(或者在循环导入的情况下正在加载)Python将不再加载。它只会引用现有的模块对象并将其放入导入命名空间。
C.py:
print 1
import B
print 3
import B
print 4
当第二个 import
语句为 运行 时,不会打印任何内容,因为 B
模块已经加载(通过第一个 import
语句)。
这是您的模块的简单版本,显示了循环导入的问题:
D.py:
print 1
import E
print 5 # this doesn't get a chance to run, nor the code below
x = 7
print 6
E.py:
print 2
import D
print 3
print D.x # this causes an exception, since D doesn't have an x attribute yet
print 4
如果您导入 D
,您将打印 1-3,然后当 E
中的代码试图访问 D
中的全局变量时出现异常尚未初始化。请注意 5
不会在异常之前打印出来,因为 D
的执行暂停等待 E
完成加载。
有几种方法可以修复代码。
首先是一个错误的修复。如果您首先导入 E
而不是 D
,则不会出现异常(尽管您会得到一些乱序打印的数字)。不过,我不建议将此作为解决方案,因为如果您在以后的代码中更改一些导入,它可能会再次中断并且非常混乱!
通常 "best" 方法是重新组织代码以消除模块之间的循环依赖。要么将一些代码从一个模块移动到另一个模块,要么将其分解到第三个模块中,这两个模块都可以导入。这种方法可能会受到学习使用其他语言编程的程序员的大力提倡,因为循环依赖总是会被打破,但这在 Python.
中并不是一个大问题。
另一种选择是允许保留循环导入,但只是为了避免在模块的顶层做太多事情。通常,您可以将麻烦的代码放入一个函数(由模块外部的代码调用),尽管有循环导入,它仍能正常工作。如果没有任何顶级代码试图访问其他模块的内容,循环导入不是问题,因为在导入完成并且所有模块都已完全加载之前,实际上并没有多少 运行 .
这是一个例子:
F.py:
print 2
import D
print 3
def foo():
print D.x # not at top level any more
print 4
main.py
import D, F
F.foo()
OTS 模块
from Tkinter import *
import openTableApiGet
#bunch of code blah blah blah
openTableApiGet.main() # call to the method in the OpenTableApiGet module
模块 OpenTableApiGet
import OTS
class Parser:
#Bunch of code in the class doing stuff
def main():
#bunch of code
#The main method the complier says this module
#doesn't have. Outside of `the Parser class,
#just hanging out on its own
为什么会这样?是循环导入的罪魁祸首我宁愿保留它,但如果我必须改变它,我会的。我需要写更多的东西来让 Whosebug 开心,所以我希望你能尽快在生活中找到炸玉米饼!
谢谢大家
您是对的,循环导入会使您显示的代码在某些情况下失败。具体来说,如果外部代码首先导入 OpenTableApiGet
模块,OTS
模块将在尝试调用 OpenTableApiGet.main()
.
原因如下。当 Python 加载模块时,它从顶部开始并且 运行 按顺序加载每个语句。对于导入语句,它可能必须暂停该模块的执行才能加载另一个模块。
这是一个例子:
A.py:
print 1
import B
print 3
B.py
print 2
这两个简单的会在你导入的时候按顺序打印数字1-3 A
。
导入语句并不总是暂停。如果要导入的模块已经加载(或者在循环导入的情况下正在加载)Python将不再加载。它只会引用现有的模块对象并将其放入导入命名空间。
C.py:
print 1
import B
print 3
import B
print 4
当第二个 import
语句为 运行 时,不会打印任何内容,因为 B
模块已经加载(通过第一个 import
语句)。
这是您的模块的简单版本,显示了循环导入的问题:
D.py:
print 1
import E
print 5 # this doesn't get a chance to run, nor the code below
x = 7
print 6
E.py:
print 2
import D
print 3
print D.x # this causes an exception, since D doesn't have an x attribute yet
print 4
如果您导入 D
,您将打印 1-3,然后当 E
中的代码试图访问 D
中的全局变量时出现异常尚未初始化。请注意 5
不会在异常之前打印出来,因为 D
的执行暂停等待 E
完成加载。
有几种方法可以修复代码。
首先是一个错误的修复。如果您首先导入 E
而不是 D
,则不会出现异常(尽管您会得到一些乱序打印的数字)。不过,我不建议将此作为解决方案,因为如果您在以后的代码中更改一些导入,它可能会再次中断并且非常混乱!
通常 "best" 方法是重新组织代码以消除模块之间的循环依赖。要么将一些代码从一个模块移动到另一个模块,要么将其分解到第三个模块中,这两个模块都可以导入。这种方法可能会受到学习使用其他语言编程的程序员的大力提倡,因为循环依赖总是会被打破,但这在 Python.
中并不是一个大问题。另一种选择是允许保留循环导入,但只是为了避免在模块的顶层做太多事情。通常,您可以将麻烦的代码放入一个函数(由模块外部的代码调用),尽管有循环导入,它仍能正常工作。如果没有任何顶级代码试图访问其他模块的内容,循环导入不是问题,因为在导入完成并且所有模块都已完全加载之前,实际上并没有多少 运行 .
这是一个例子:
F.py:
print 2
import D
print 3
def foo():
print D.x # not at top level any more
print 4
main.py
import D, F
F.foo()