从另一个模块访问模块的功能,该模块本身导入第一个模块

时间:2012-11-19 10:09:06

标签: python-2.7 tkinter importerror

很抱歉这个令人困惑的标题,我试图在标题中解决这个问题。


场景:

我有一个模块(比如ModuleA),它包含一些Tkinter GUI元素的类定义。 这些元素还具有与它们绑定/绑定的某些事件/函数(例如'<Button-1>','<Button-2>'...

现在还有另一个模块(比如ModuleB)。这是该计划的主要(核心)。在此模块中,我导入ModuleA以使用其对象。 ModuleA的对象在数组中有一个位置(比如Array1);还有另一个数组(比如Array2),它存储Array1每个对象的一个​​数据成员的值,这些是由数据成员操作的数据成员。 Event Bindings

所以问题是当Event发生时,Array1的对象(存储在ModuleA中)会根据需要进行视觉响应,但在后端,它们对应的数据成员值必须也会相应地更新Array2


简而言之:

#ModuleA.py

from ModuleB import foo

class bar
    data = 1
    # some tkinter code 
    # bind mouse click to function foo of ModuleB

-------------------------------------------------------

#ModuleB.py

from ModuleA import bar

Array1 = [objects of class bar]
Array2 = [value of data of objects in Array1]

def foo(#obj of class bar)
    # find index of bar object which called this function in Array1
    # accordingly change Array2

我尝试了什么:

Event Bindings对象的ModuleA中,我添加了所需的函数(比如foo),它将处理所需的数组操作,并在ModuleB中定义,因为它必须处理Array2的{​​{1}}。 但这给了我一个错误ModuleB

因此,在global name 'foo' is not defined的类定义中,我添加了ModuleA,这也没有解决它。

最后我尝试插入global foo

ModuleA

提出from ModuleB import foo 说它无法导入foo(我猜是因为ImportError本身正在导入ModuleB因此循环引用)


解决方案

一个清晰可见的解决方案是将整个ModuleA(包含类定义)复制到ModuleA。 但这并不总是实用的,也不是太过pythonic。

请帮忙。

3 个答案:

答案 0 :(得分:1)

实际上你做错了,因为假设有两个模块 moduleA和moduleB。如果将moduleA导入moduleB,则无法导入 moduleB进入moduleA,如果你想做那件事你应该得到导入错误。

让我们看看你正在做的错误方式

以下是moduleA.py

import moduleB
print " I am moduleA"
class foo():
     pass

这是moduleB.py

from  moduleA import foo
print "I am moduleB"

现在执行moduleB.py

python moduleB.py

您将获得ImportError,但如果您执行

则不会发生
python moduleA.py 

上面的过程是退出错误的,python不允许这种类型的导入 简单地说,我们正在创建导入循环,因为在每次首次导入时,整个模块的顶级代码将被执行,当然python将不允许这样做。

要解决您的问题,只需更改模块层次结构。

这是一个简单的解决方案: 如果您想要两个模块(moduleA和moduleB)的内容,只需创建一个模块 moduleC并在moduleC中导入两个模块(moduleA和moduleB)并执行必要的操作。

请参考编辑后的内容,你正在做同样的事情

答案 1 :(得分:1)

Python中的循环导入可能令人困惑。这很容易弄错,所以如果可能的话,通常最好避免它们。但是,它们并非违法,如果你小心,它们可以正常工作。

当您输入import foo

时,导入过程就是这样的
  1. 首先,Python检查sys.modules字典以查看模块foo是否已经加载。如果是这样,那么预先存在的模块就会被放置到当前模块的命名空间中(并且进程就会停止)。
  2. 如果没有,它会检查是否可以加载foo.py文件。如果不存在,则引发ImportError并且该过程停止。
  3. 如果找到了文件,则会创建一个新的空模块对象,并以sys.modules的名称放入foo
  4. 然后,Python打开文件并开始执行它,使用在步骤3中创建的模块对象作为本地命名空间。
  5. 现在,如果上面描述的foo模块有自己的import语句,导入过程可以递归,加载另一个模块,然后继续执行foo模块码。但是,由于在处理任何更多导入之前,空模块对象被添加到sys.modules字典中,因此它将永远不会循环并尝试多次加载同一文件(在大多数情况下)。

    有些事情可能会出错。

    如果foo作为脚本执行(而不是由其他模块导入),sys.modules中的初始条目将以__main__而不是{{1 }}。这意味着如果从其他地方导入foo,您最终将获得相同代码的两个副本。这是模块不会被加载两次的一般规则的例外,在某些情况下它会让你措手不及。

    此外,如果您的循环导入的模块正在相互进行顶级访问,如果它们以错误的顺序导入(或任何顺序,如果它们都引用了错误的相互内容,则会遇到麻烦)从顶层的方式)。您可以通过确保尽可能少的代码在模块加载时运行来避免大多数问题(将其放入函数中!)。然后在加载完所有内容后,从一个定义良好的入口点调用函数。

    所以,在你的情况下,这是我要检查的事情:

    1. 你需要做foo吗?您可以改为from moduleB import foo并稍后访问import moduleB吗?这将是最容易解决的问题(尽管它不会解决所有可能出现的问题)。
    2. 您是否需要在模块的顶层工作?通常,您可以将对象创建放入工厂函数中,而不是放在顶层(这使得解决方案#1更频繁地工作)。无论如何,这可能是一个很好的设计理念,因为如果您希望能够改变(或自定义)它们在运行时创建对象的方式,则以后更容易扩展(您只需编写第二个以不同方式工作的函数,或者添加对工厂的争论。)
    3. 您是否可以将部分代码从moduleB.fooModuleA移动到第三个模块中,而不需要导入其中任何一个模块,从而无需进行循环导入?这通常是一个好主意,因为它可以让你回避在什么时候加载的内容的整个问题。
    4. 编辑:这是模块的固定版本:

      ModuleA.py:

      ModuleB

      ModuleB.py:

      import ModuleB
      
      class bar():
          def __init__(self, data):
              self.data = data
              # do something here that accesses ModuleB.foo
      

      main.py:

      import ModuleA
      
      def foo():
          # do whatever
      
      Array1 = [ModuleA.bar(i) for i in range(10)]
      Array2 = [whatever]
      

      这应该是最小的修复。 ModuleA仅从import ModuleB # import order is important here! import ModuleA if __name__ == "__main__": # do stuff 类的ModuleB.foo方法中访问bar,因此只要在任何__init__实例之前定义ModuleB.foo,循环导入就会正常工作创建

      如果您希望导入按任何顺序工作,那就有点棘手了。您不希望在模块的顶层完成任何工作:

      ModuleA.py如上所述。

      ModuleB.py:

      bar

      main.py:

      import ModuleA
      
      def foo():
          #do stuff
      
      def setupArrays():
          global Array1, Array2 # lets us create these global variables
      
          Array1 = [ModuleA.bar(i) for i in range(10)]
          Array2 = [whatever]
      

      这仍然是一个问题。如果你可以完全打破循环导入,可能会以更明显的方式简化代码。例如,我们可以将回调函数作为参数传递给import ModuleA, ModuleB # import order doesn't matter if __name__ == "__main__": ModuleB.setupArrays() # do stuff 类的构造函数:

      ModuleA.py:

      bar

      ModuleB:

      # No import statement here! Circular imports avoided!
      
      class bar():
          def __init__(self, data, callback):
              self.data = data
              # bind stuff to call the callback function provided
      

答案 2 :(得分:0)

您应该检查此链接:http://effbot.org/zone/import-confusion.htm 有一个有趣的段落关于如何通过移动模块末尾的import语句来处理循环引用。但恕我直言不会试图处理那件事,只是重构你的代码;)