Неожиданное поведение паттерна Борг при использовании в двух разных модулях - PullRequest
1 голос
/ 11 сентября 2009

Я использую паттерн Борг с взаимным включением модулей. См. Пример кода (не настоящий код, но он показывает проблему) ниже. В этом случае у меня есть два разных 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__.Borg и borg.Borg как два разных класса?

Ответы [ 5 ]

4 голосов
/ 04 января 2010

После долгого дня борьбы с синглетонами и боргами мой вывод следующий:

Кажется, что модуль Python, импортированный несколько раз с использованием разных «путей импорта», на самом деле импортируется несколько раз. Если этот модуль содержит синглтон, вы получите несколько экземпляров.

Пример:

myproject/
  module_A
  some_folder/
    module_B
    module_C

Если module_A импортирует module_C с использованием from myproject.some_folder import module_C, а module_B импортирует тот же module_C с использованием import module_C, модуль фактически импортируется дважды (по крайней мере, согласно моим наблюдениям). Обычно это не имеет значения, но для синглетонов или боргов вы получаете 2 экземпляра того, что должно быть уникальным. (Это 2 набора боргов с двумя различными внутренними состояниями).

Решение: Дайте себе соглашение об утверждении импорта и придерживайтесь его: я импортирую все модули, начиная с общей корневой папки, даже если файл модуля расположен параллельно тому, над которым я работаю, поэтому в приведенном выше примере оба module_A и module_B импортируют module_C, используя from myproject.some_folder import module_C.

3 голосов
/ 11 сентября 2009

Проблема возникает только в вашей основной функции. Переместить этот код в свой собственный файл, и все так, как вы ожидаете. Этот код

import borg
import borg2

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

выдаёт этот вывод:

borg.Borg 10438672
borg.Borg 10438672
Bjorn
borg.Borg 10438672
Bjorn
1 голос
/ 11 сентября 2009

Проблема не в именах классов. Я не совсем уверен, почему 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

Это решает проблему.

0 голосов
/ 17 августа 2016

Решение - как уже упоминалось - состоит в том, чтобы избежать рекурсивного import основного модуля, но borg.py означает , а не , который "импортируется дважды". Проблема в том, что при импорте на всех , когда он уже выполняется, вы определяете класс Borg дважды в двух разных пространствах имен.

Для демонстрации я добавил несколько строк вверху borg.py и borg2.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 и функцию fct_ab в пространстве имен borg и завершает работу.

borg2 затем определяет fct_c и выходит ("borg2: Конец borg2.py."). Все операторы import выполнены.

Теперь borg.py наконец выполнится «по-настоящему». Да, он уже запускался один раз, когда импортировался, но это все еще «первый раз» в файле borg.py. Класс Borg снова определяется, на этот раз в пространстве имен __main__, и у класса, и у его словаря новые идентификаторы.

borg.py не был "импортирован дважды". Он был выполнен один раз из командной строки и один раз был выполнен при импорте. Поскольку это произошло в двух разных пространствах имен, «второе» определение Borg не заменило первое, и две функции изменили два разных класса, которые просто оказались созданы из одного и того же кода.

0 голосов
/ 11 сентября 2009

Я исправил проблему в моем реальном приложении, исправив ошибку в импорте.

На самом деле, у меня есть два разных модуля, использующих один и тот же третий модуль.

Первый импортировал mypackage.mymodule, а второй импортировал mymodule. mypackage установлен как python egg, а код, над которым я работал, находится в моей папке для разработки.

Итак, оба кода импортировали разные модули, и я думаю, что в этом случае нормально иметь два разных класса.

Что касается примера кода, который я использовал, проблема заключается в том, что текущие модули получают имя main в качестве имени. Я пытался переименовать, выполнив __name__ = 'borg'. Это работает, но нарушает условие if __name__ == "__main__". В заключение я бы сказал, что следует избегать взаимного включения и в большинстве случаев не является необходимым.

Спасибо всем за помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...