Pythonic 方式来解决循环导入语句?
我刚刚继承了一些让我感到不安的代码:有一个测试库,里面充满了与我们网站上的网页相对应的类,并且每个网页类都有自动执行该页面上的功能的方法。
有一些方法可以单击页面之间的链接,该方法返回链接页面的类。这是一个简化的示例:
File 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()
File 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>
所以,我特别感到最不安的部分about 是最终结束的 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 方法来处理这样的循环依赖,并且仍然将 import 语句保留在文件顶部?
I just inherited some code which makes me uneasy: There is a testing library, full of classes corresponding to webpages on our site, and each webpage class has methods to automate the functionality on that page.
There are methods to click the link between pages, which returns the class of the linked page. Here's a simplified example:
File 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()
File 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()
This then allows the script file to click on pages and get the object as a return value from that method, meaning the script author won't have to keep instantiating new pages as they navigate around the site. (I feel like this is an odd design, but I can't exactly put my finger on why, other than that it seems strange to have a method named 'clickSomeLink' and return an object of the resulting page.)
The following script illustrates how a script would navigate around the site: (I inserted print page
to show how the page object changes)
Script File:
from homePageLib import HomePage
page = HomePage()
print page
page = page.clickCalendarLink()
print page
page = page.clickHomePageLink()
print page
which produces the following output:
<homePageLib.HomePage object at 0x00B57570>
Click Calendar link
<calendarLib.CalendarPage object at 0x00B576F0>
Click Home Page link
<homePageLib.HomePage object at 0x00B57570>
So, the part of this that I specifically feel most uneasy about are the from ____ import ____
lines that end up all over. These strike me as bad for the following reasons:
- I've always made it a convention to put all import statements at the top of a file.
- Since there may be multiple links to a page, this results in the same
from foo import bar
line of code in several places in a file.
The problem is, if we put these import statements at the top of the page, we get import errors, because (as per this example), HomePage imports CalendarPage and vice versa:
File 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()
File 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()
This results in the following error:
>>> 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
(tips on how to better format python output?)
Rather than perpetuating this style, I'd like to find a better way. Is there a Pythonic way to deal with circular dependencies like this and still keep import statements at the top of the file?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
解析这些构造通常涉及诸如依赖注入之类的技术。
然而,修复此错误相当简单:
在calendarLib.py中:
模块级别的代码在导入时执行。使用
from [...] import [...]
语法要求模块完全初始化才能成功。简单的
import [...]
则不会,因为没有访问任何符号,从而破坏了依赖链。Resolving these constructs usually involves techniques like Dependency Injection.
It is, however, rather simple to fix this error:
In calendarLib.py:
The code at module level is executed at import time. Using the
from [...] import [...]
syntax requires the module to be completely initialized to succeed.A simple
import [...]
does not, because no symbols are accessed, thus breaking the dependency chain.我有一个循环导入,因为我在类型提示中引用了一个类。
这可以使用 from __future__ import 注解来解决(使用 Python 3.9.x 测试)。
示例:
AClass.py
BClass.py
I have a circular import because I reference a class in a type hint.
This can be solved using
from __future__ import annotations
(tested with Python 3.9.x).Example:
AClass.py
BClass.py
请阅读Sebastian 的回答以获取详细说明。这种方法是由 David Beazley 在 PyCon 中提出的。
尝试将导入定位在顶部,如下所示
这将尝试导入您的
HomePage
,如果失败,将尝试从缓存加载它Please read Sebastian's answer for detailed explanation. This approach was proposed by David Beazley in PyCon
Try positioning the imports on the top like this
This will try to import your
HomePage
and if failed, will try to load it from the cache这里尚未提及的另一种方法:将导入放在类定义下方。
适用于 Python 2 和 3。
Another method not yet mentioned here: put the imports below the class definitions.
Works in both Python 2 and 3.