在两个不同的模块中使用时,Borg模式出现意外行为

时间:2009-09-11 09:21:49

标签: python import

我正在使用Borg模式,相互包含模块。请参阅下面的示例代码(不是真实代码,但它显示了问题)。在这种情况下,我有两个不同的Borgs,因为类名(我猜这个类)被解释器视为不同。

在没有重新设计模块架构的情况下,有没有办法在这种情况下使用Borg?

模块borg.py

import borg2

class Borg:
    _we_are_one = {}

    def __init__(self):
        self.__dict__ = Borg._we_are_one
        try:
            self.name
        except AttributeError:
            self.name = "?"
        print self.__class__, id(self.__dict__)

def fct_ab():
    a = Borg()
    a.name = "Bjorn"

    b = Borg()
    print b.name

if __name__ == "__main__":
    fct_ab()
    borg2.fct_c()

模块borg2.py

import borg

def fct_c():
    c = borg.Borg()
    print c.name

结果是

__main__.Borg 40106720
__main__.Borg 40106720
Bjorn
borg.Borg 40106288
?

为了澄清我的问题: 为什么Python将__main__.Borgborg.Borg视为两个不同的类?

5 个答案:

答案 0 :(得分:4)

经过漫长的一天与Singletons和Borg的斗争后,我的结论如下:

似乎使用不同的“导入路径”多次导入的Python模块实际上是多次导入的。如果该模块包含单例,则会获得多个实例。

示例:

myproject/
  module_A
  some_folder/
    module_B
    module_C

如果module_A使用from myproject.some_folder import module_C导入module_C并且module_B使用import module_C导入相同的module_C,则模块实际导入两次(至少根据我的观察)。通常,这没关系,但对于单身人士或博格,你实际上得到了2个应该是唯一的实例。 (这是两组共享两个不同的内部状态)。

解决方案:给自己一个import语句约定并坚持下去:我从一个公共根文件夹开始导入所有模块,即使模块文件与我正在处理的模块文件平行,所以在上面的例子中,两者都是module_A和module_B使用from myproject.some_folder import module_C导入module_C。

答案 1 :(得分:3)

问题只发生在你的主要功能中。移动该代码 到它自己的文件,一切都如你所料。这段代码

import borg
import borg2

if __name__ == "__main__":
    borg.fct_ab()
    borg2.fct_c()

提供此输出:

borg.Borg 10438672
borg.Borg 10438672
Bjorn
borg.Borg 10438672
Bjorn

答案 2 :(得分:1)

问题不在于班级名称。我不完全确定为什么Python看到Borg类和borg.Borg类不同,也许是因为你从__main__运行它,我认为python没有意识到__main__和borg是相同的模块。

解决方案很简单。将fct_ab更改为:

def fct_ab():
    import borg
    a =  borg.Borg()
    a.name = "Bjorn"

    b = borg.Borg()
    print b.name

这解决了这个问题。

答案 3 :(得分:0)

我通过修复导入中的错误修复了我的实际应用中的问题。

事实上,我有两个不同的模块使用相同的第三个模块。

第一个导入mypackage.mymodule而第二个导入mymodule。 mypackage作为python egg安装,我正在处理的代码在我的开发文件夹中。

所以这两个代码都导入了不同的模块,我想在这种情况下有两个不同的类是正常的。

关于我使用的示例代码,问题来自当前接收 main 作为名称的模块。我尝试通过__name__ = 'borg'重命名。它有效,但它打破了if __name__ == "__main__"的意见。作为结论,我想说必须避免相互包容,在大多数情况下是不必要的。

感谢大家的帮助。

答案 4 :(得分:0)

解决方案---正如已经提到的那样---是为了避免主模块的递归import,但是borg.py 是&#34 ;进口两次"。问题是在它已经执行时导入 at 导致你在两个不同的命名空间中定义Borg类两次。

为了演示,我在borg.pyborg2.py的顶部添加了几行,并在大多数兴趣点之前和之后插入了我的print_module函数:

#!/usr/bin/env python2

from __future__ import print_function

def print_module(*args, **kwargs):
    print(__name__ + ':  ', end='')
    print(*args, **kwargs)
    return

print_module('Importing module borg2...')
import borg2
print_module('Module borg2 imported.')

print_module('Defining class Borg...')
class Borg:
    ...
# etc.

输出结果为:

__main__:  Importing module borg2...
borg2:  Importing module borg...
borg:  Importing module borg2...
borg:  Module borg2 imported.
borg:  Defining class Borg...
borg:  id(_we_are_one) = 17350480
borg:  Class Borg defined.
borg:  id(Borg) = 139879572980464
borg:  End of borg.py.
borg2:  Module borg imported.
borg2:  End of borg2.py.
__main__:  Module borg2 imported.
__main__:  Defining class Borg...
__main__:  id(_we_are_one) = 17351632
__main__:  Class Borg defined.
__main__:  id(Borg) = 139879572981136
__main__:  Borg 17351632
__main__:  Borg 17351632
__main__:  Bjorn
borg:  Borg 17350480
borg2:  ?
__main__:  End of borg.py.

borg.py做的第一件事(不计算我添加的位)是导入borg2__main__命名空间。这发生在任何地方定义Borg类之前。

borg2做的第一件事就是导入borg,它再次尝试导入borg2 ......并且Python拒绝这样做。 (注意 nothing 在第3行和第4行之间发生。)borg最终在Borg命名空间中定义fct_ab类和borg函数,并退出。

然后

borg2定义fct_c并退出(" borg2:borg2.py的结尾。")。所有import语句都已完成。

现在,borg.py 终于执行" for real"。是的,它在导入时已经运行过一次,但这仍然是第一个"通过borg.py文件的时间。 Borg类再次定义,这次是在__main__命名空间中,类及其字典都有新的ID。

borg.py没有"两次输入"。它从命令行执行一次,并在导入时执行一次。由于这些发生在两个不同的命名空间中," second" Borg的定义没有替换第一个,两个函数修改了两个不同的类,恰好是从同一个代码创建的。