Подтвердить что ты не робот

Pythonic способ решения круговых операторов импорта?

Я просто унаследовал какой-то код, который вызывает у меня беспокойство: на нашем сайте есть тестовая библиотека, полная классов, соответствующих веб-страницам, и каждый класс веб-страницы имеет методы автоматизации функций на этой странице.

Есть способы щелкнуть ссылку между страницами, которая возвращает класс связанной страницы. Здесь упрощенный пример:

Файл homePageLib.py:

class HomePage(object):
    def clickCalendarLink(self):
        # Click page2 link which navigates browswer to page2
        print "Click Calendar link"
        # Then returns the page2 object
        from calendarLib import CalendarPage
        return CalendarPage()

Файл calendarLib.py:

class CalendarPage(object):
    def clickHomePageLink(self):
        # Click page1 link which navigates browswer to page1
        print "Click Home Page link"
        # Then return the page2 object
        from homePageLib import HomePage
        return HomePage()

Затем это позволяет script файлу щелкнуть по страницам и получить объект в качестве возвращаемого значения из этого метода, что означает, что автору script не придется создавать экземпляры новых страниц при навигации по сайту. (Я чувствую, что это странный дизайн, но я не могу точно сказать, почему, кроме этого, кажется странным иметь метод с именем "clickSomeLink" и возвращать объект результирующей страницы.)

Следующий script иллюстрирует, как script будет перемещаться по сайту: (я вставил print page, чтобы показать, как изменяется объект страницы)

Script Файл:

from homePageLib import HomePage

page = HomePage()    
print page
page = page.clickCalendarLink()
print page
page = page.clickHomePageLink()
print page

который производит следующий вывод:

<homePageLib.HomePage object at 0x00B57570>
Click Calendar link
<calendarLib.CalendarPage object at 0x00B576F0>
Click Home Page link
<homePageLib.HomePage object at 0x00B57570>

Таким образом, часть этого, о которой я особенно беспокоюсь, - это строки from ____ import ____, которые заканчиваются на всем протяжении. Они поражают меня так же плохо по следующим причинам:

  • Я всегда делал это соглашением, чтобы все операторы импорта отображались в верхней части файла.
  • Так как может быть несколько ссылок на страницу, это приводит к той же строке кода from foo import bar в нескольких местах в файле.

Проблема заключается в том, что если мы помещаем эти операторы импорта в начало страницы, мы получаем ошибки импорта, потому что (согласно этому примеру) HomePage импортирует CalendarPage и наоборот:

Файл homePageLib.py

from calendarLib import CalendarPage

class HomePage(object):
    def clickCalendarLink(self):
        # Click page2 link which navigates browswer to page2
        print "Click Calendar link"
        # Then returns the page2 object

        return CalendarPage()

Файл calendarLib.py

from homePageLib import HomePage

class CalendarPage(object):
    def clickHomePageLink(self):
        # Click page1 link which navigates browswer to page1
        print "Click Home Page link"
        # Then return the page2 object
        return HomePage()

Это приводит к следующей ошибке:

>>> from homePageLib import HomePage
Traceback (most recent call last):
  File "c:\temp\script.py", line 1, in ?
    #Script
  File "c:\temp\homePageLib.py", line 2, in ?
    from calendarLib import CalendarPage
  File "c:\temp\calendarLib.py", line 2, in ?
    from homePageLib import HomePage
ImportError: cannot import name HomePage

(советы о том, как лучше форматировать вывод python?)

Вместо того, чтобы увековечить этот стиль, я хотел бы найти лучший способ. Есть ли способ Pythonic справиться с круговыми зависимостями, подобными этому, и сохранять инструкции импорта в верхней части файла?

4b9b3361

Ответ 1

Разрешение этих конструкций обычно включает в себя такие методы, как Injection of Dependency.

Однако довольно легко исправить эту ошибку:

В calendarLib.py:

import homePageLib

class CalendarPage(object):
    def clickHomePageLink(self):
        [...]
        return homePageLib.HomePage()

Код на уровне модуля выполняется во время импорта. Использование синтаксиса from [...] import [...] требует, чтобы модуль был полностью инициализирован для успеха.

Простой import [...] не является, поскольку никакие символы не доступны, тем самым нарушая цепочку зависимостей.

Ответ 2

Прочтите ответ Себастьяна для подробного объяснения. Этот подход был предложен Дэвидом Бэйсли в PyCon

Попробуйте позиционировать импорт вверху, как это показано

try:
    from homePageLib import HomePage
except ImportError:
    import sys
    HomePage = sys.modules[__package__ + '.HomePage']

Это попытается импортировать ваш HomePage, а если не удалось, попытается загрузить его из кеша

Ответ 3

Да. Рефакторинг, чтобы разбить вещи на более мелкие, независимые части.