关于Python的重构:重构该如何圆

Python: How to refactor circular imports

我有一件事你可以做engine.setState(),它将实例化你给它的类类型,并开始在新状态下运行。

SelectFileState中有一个到NewFileState的按钮,在NewFileState上有一个回到SelectFileState的按钮。

现在,在SelectFileState的开头,我要进口NewFileState(这样我可以稍后在课堂上做engine.setState(NewFileState)。在NewFileState的开始,我也在进口SelectFileState(这样我以后可以回到SelectFileState了)。

但是,这会创建一个循环导入,如其他一些文章中所述。有人说循环导入是设计不好的指标,应该重构。

我知道我可以在需要使用它之前通过导入EDOCX1[1]来解决这个问题,但我宁愿以正确的方式进行操作并重构它。

现在我想知道……你将如何重构它?

谢谢。

编辑:Pydsigner建议我将这两个文件合并为一个,因为它们彼此都非常相关。但是,我不能将每个具有循环依赖性的状态都放到一个文件中,因此必须有更好的方法来实现这一点。有什么想法吗?

2编辑:我现在通过不使用from x import y语法来避开这个问题,而只使用import x。这不是一个更好的解决方案,我想知道"Python"的方法来解决这类问题。仅仅将文件合并在一起并不能永远解决问题。

代码:

选择文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from states.state import State
from states.newfilestate import NewFileState

from elements.poster import Poster
from elements.label import Label
from elements.button import Button
from elements.trifader import TriFader

import glob
import os

class SelectFileState(State):
    def __init__(self, engine):
        super().__init__(engine)

    def create(self):
        self.engine.createElement((0, 0), Poster(self.engine.getImage('gui_loadsave')), 1)
        self.engine.createElement((168, 30), Label("Load a game", 40), 2)
        self.engine.createElement((400, 470), Button("New save", code=self.engine.createElement, args=((0, 0), TriFader(NewFileState, False), -240)), 3)

        ycounter = 150

        globs = glob.glob("save\\*.mcw")
        for file in globs:
            self.engine.createElement((200, ycounter), Button(os.path.basename(file)[:-4]), 2)
            ycounter += 50

新文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from states.state import State
from states.selectfilestate import SelectFileState

from elements.poster import Poster
from elements.label import Label
from elements.button import Button
from elements.inputbox import InputBox
from elements.trifader import TriFader


class NewFileState(State):
    def __init__(self, engine):
        super().__init__(engine)

    def create(self):
        self.engine.createElement((0, 0), Poster(self.engine.getImage('gui_loadsave')), 1)
        self.engine.createElement((135, 30), Label("Make a new save", 40), 2)

        self.lvlname = self.engine.createElement((180, 212), InputBox(length=25, text="World name"), 2)
        self.engine.createElement((200, 240), Button(text="Ok", code=self.createSave, args=()), 2)

    def createSave(self):
        open("save\" + self.lvlname.getText() +".mcw", 'w')
        self.engine.createElement((0, 0), TriFader(SelectFileState), -240)


如果看不到代码,最有意义的是合并两个文件。如果它们紧密地交织在一起,你可能会把它们放在一起,而不会有任何异常的地方。


在python中,导入不必出现在模块的开头。实际上,它们可以出现在函数中,因此在newfilestate.py中,可以将selectfilestate的导入移动到newfilestate.create中,并且可以对selectfilestate.py进行类似的更改。


您可以在字典映射中隐藏实际的类引用,而不是传递类引用,而是传递一个映射到实际类的常量值。它可以像dict一样保存在独立模块中,也可以包装在单独的StateManager类中,该类负责检索下一个状态。

这种方法的问题在于,您需要手动更新状态列表以及从常量到类引用的映射。

下面是一个示例实现:

康斯特

1
2
3
4
5
6
# This module is states.const

(
    STATE_SELECT_FILE,
    STATE_NEW_FILE,
) = range(2) # manually update this number when you add/remove states

状态映射

1
2
3
4
5
6
7
8
9
# This module is states.mapping
from states.const import *
from states.newfilestate import NewFileState
from states.selectfilestate import SelectFileState

STATE_MAPPING = {
    STATE_SELECT_FILE : SelectFileState,
    STATE_NEW_FILE : NewFileState,
}

选择文件

1
2
3
from states.const import STATE_NEW_FILE
# ... snip ...
... TriFader(STATE_NEW_FILE, False) ...

新文件

1
2
3
from states.const import STATE_SELECT_FILE
# ... snip ...
... TriFader(STATE_SELECT_FILE) ...

发动机.设置状态(

1
2
3
4
5
from states.mapping import STATE_MAPPING

def setState(class_key):
    obj = STATE_MAPPING[class_key]()
    # ... do other stuff ...

您可以在这里做的是,因为只有在代码运行、类被实例化和方法被调用时,您才需要交互类,这是为了使类名称可用于父模块-"状态",并从中导入您的名称:

状态

1
2
from states.selectfilestate import SelectFileState
from states.newfilestate import NewFileState

此时,两个子模块都将被初始化,即使在他们将"看到"一个不完整版本的states的尸体。模块,此阶段无法访问此模块。当任何一个女同学实际实例化,EDOCX1[1]将完成执行,模块引用states将指向完整的模块对象。

在state.selectfilestate中:

1
2
3
import states
...
        self.engine.createElement((0, 0), TriFader(states.SelectFileState), -240)

在state.newfilestare上:

1
2
3
import states
...
        self.engine.createElement((0, 0), TriFader(SelectFileState), -240)