解决循环导入语句的Pythonic方法?

问题描述 投票:0回答:4

我刚刚继承了一些让我不安的代码:有一个测试库,里面充满了与我们网站上的网页相对应的类,并且每个网页类都有方法来自动化该页面上的功能。

有一些方法可以点击页面之间的链接,它返回链接页面的类。这是一个简化的示例:

文件 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()

然后,这允许脚本文件单击页面并获取对象作为该方法的返回值,这意味着脚本作者在浏览网站时不必继续实例化新页面。 (我觉得这是一个奇怪的设计,但我无法确切地指出为什么,除此之外,有一个名为“clickSomeLink”的方法并返回结果页面的对象似乎很奇怪。)

以下脚本说明了脚本如何在站点中导航:(我插入

print page
以显示页面对象如何变化)

脚本文件:

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 ____
线。这些让我觉得很糟糕,原因如下:

  1. 我一直将所有导入语句放在文件顶部作为惯例。
  2. 由于一个页面可能有多个链接,因此这会导致文件中的多个位置出现相同的
    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 方法来处理这样的循环依赖,并且仍然将 import 语句保留在文件顶部?

python pageobjects
4个回答
122
投票

解决这些构造通常涉及依赖注入等技术。

但是,修复此错误相当简单:

在calendarLib.py中:

import homePageLib

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

模块级别的代码在导入时执行。使用

from [...] import [...]
语法需要模块完全初始化才能成功。

一个简单的

import [...]
则不会,因为没有访问任何符号,从而破坏了依赖链。


10
投票

我有一个循环导入,因为我在类型提示中引用了一个类。 这可以使用

from __future__ import annotations
解决(使用 Python 3.9.x 测试)。

示例:

AClass.py

from BClass import BClass
class AClass():
    def __init__(self) -> None:
        self.bClass = BClass(self)

BClass.py

from __future__ import annotations  # Without this, the type hint below would not work.
import AClass  # Note that `from AClass import AClass` would not work here.`
class BClass:
    def __init__(self, aClass: AClass.AClass) -> None:
        self.aClass = aClass

1
投票

请阅读Sebastian的回答以获取详细解释。这种方法是由 David Beazley 在 PyCon

中提出的

尝试将导入放置在顶部,如下所示

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

这将尝试导入您的

HomePage
,如果失败,将尝试从缓存加载它


0
投票

这里尚未提及的另一种方法:将导入放在类定义下方。

# 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
        return CalendarPage()
        
from calendarLib import 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
        return HomePage()

from homePageLib import HomePage

适用于 Python 2 和 3。

© www.soinside.com 2019 - 2024. All rights reserved.