【问题标题】:Borg pattern unexpected behaviour when used in two different modules在两个不同的模块中使用 Borg 模式时的意外行为
【发布时间】:2009-09-11 09:21:49
【问题描述】:

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

有没有办法在这种情况下使用 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 视为两个不同的类?

【问题讨论】:

  • 你们真的有相互进口吗? borg2 是否导入 borg? borg 是否导入 borg2?为什么会有这些相互导入?
  • 事实上,我没有这些相互导入,但我成功地模拟了这样的问题。我有 2 个模块正在导入相同的模块,但它们使用不同的名称。
  • 一堆实例共享相同状态/__dict__的实际用例是什么?唯一能区分它们的是它们的 id() (如果有的话,可能还有 __slots__。)

标签: python import


【解决方案1】:

在与 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 个应该是唯一的实例。 (即 2 组 borgs 共享 2 个不同的内部状态)。

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

【讨论】:

    【解决方案2】:

    问题仅出现在您的主函数中。移动该代码 到它自己的文件,一切都如你所料。这段代码

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

    提供此输出:

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

    【讨论】:

    • 它可以工作,但我必须重新设计我的模块架构。在我的真实应用中稍微复杂一点。
    • 我已经接受了我提出的问题的答案。
    【解决方案3】:

    问题不在于类名。我不完全确定为什么 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
    

    这样就解决了问题。

    【讨论】:

    • 在我的情况下没有任何区别:-(
    • 好吧,无论如何,接受的答案是一种更清洁的方式。
    【解决方案4】:

    我已通过修复导入中的错误解决了实际应用程序中的问题。

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

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

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

    关于我使用的示例代码,问题来自当前模块接收 ma​​in 作为名称。我试图通过__name__ = 'borg' 重命名。它有效,但它打破了if __name__ == "__main__" 条件。作为结论,我想说必须避免相互包含,而且在大多数情况下没有必要。

    感谢大家的帮助。

    【讨论】:

      【解决方案5】:

      解决方案 --- 正如已经提到的 --- 是避免主模块的递归 import,但 borg.py 不是被“导入两次”。问题在于,在它已经执行的同时根本导入它会导致您在两个不同的命名空间中定义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 拒绝这样做。 (注意,在第 3 行和第 4 行之间什么都没有发生。)borg 最后在borg 命名空间中定义了Borg 类和fct_ab 函数,然后退出。

      borg2 然后定义fct_c 并退出(“borg2: End of borg2.py.”)。所有import 语句都已完成。

      现在,borg.py终于可以“真正地”执行了。是的,它在导入时已经运行过一次,但这仍然是通过borg.py 文件的“第一次”。 Borg 类被再次定义,这次是在 __main__ 命名空间中,并且该类及其字典都有新的 ID。

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

      【讨论】:

        猜你喜欢
        • 2012-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-22
        • 1970-01-01
        • 2013-08-16
        • 1970-01-01
        相关资源
        最近更新 更多