python 导入循环依赖(可能还有函数声明)

python import circular dependency (and perhaps function declaration)

你好,除了加倍代码之外,我确实陷入了循环依赖,这是不可重构的。

我有这样的东西(只是更复杂):

myParser.py:

import sys
import main                      #comment this to make it runnable
def parseEvnt():
    sys.stdout.write("evnt:")
    main.parseCmd(1)             #comment this to make it runnable

tbl.py:

import myParser
tblx = {
       1:("cmd",),
       2:("evnt",myParser.parseEvnt),
       }

main.py:

import tbl
def parseCmd(d):
    print(tbl.tblx[d][0])
data=[1,2]
for d in data:
    if(d<2):
        parseCmd(d)
    else:
        fce = tbl.tblx[d][1]
        fce()    

我得到的明显错误是:

File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 1, in <module>
    import tbl
  File "D:\Data\vbe\workspace\marsPython\testCircular2\tbl.py", line 1, in <module>
    import myParser
  File "D:\Data\vbe\workspace\marsPython\testCircular2\myParser.py", line 2, in <module>
    import main
  File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 7, in <module>
    parseCmd(d)
  File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 3, in parseCmd
    print(tbl.tblx[d][0])
AttributeError: module 'tbl' has no attribute 'tblx'

在 C 中,我想我会通过 tbl.py 中的声明告诉我,嘿,有函数 parseEvnt()。我不需要包含 myParser 并且不会有循环包含。

在python不知道怎么做。

我读了几篇文章,总有一些聪明人建议重构。但在这种情况下 parseCmd() 需要查看 tblx 需要查看 parseEvnt() (除非函数声明)并且 parseEvnt() 需要调用 parseCmd() (cos evnt 包含触发 cmd 并且我不想将解码 cmd 代码加倍)。

有没有办法让它在 python 中工作?

应避免循环导入。重构是必需的,任何仍然需要循环导入的解决方法都不是一个好的解决方案。

话虽这么说,重构不一定要广泛。至少有几个相当简单的解决方案。

解决方案 1:将共享函数移动到共享模块

由于您想从多个地方使用 parseCmd,请将其移动到一个单独的文件中。这样 main.pymyParser.py 都可以导入函数。

解决方案 2:让主通道 parseCmdparseEvnt

首先,让 parseEvnt 接受一个参数来告诉它哪个函数要 运行:

# myParser.py
import sys
def parseEvnt(parseCmd):
    sys.stdout.write("evnt:")
    parseCmd(1) 

接下来,当您调用 myParser.parseEvnt 时,传入对 main.parseCmd 的引用:

# main.py:

...
else:
    fce = tbl.tblx[d][1]
    fce(parseCmd) 

还有其他方法可以完成同样的事情。例如,您可以在 myParser 中添加一个 "configure" 方法,然后让 main.py 调用 configure 方法并传入对其 parseCmd 的引用。然后 configure 方法可以将此引用存储在全局变量中。

另一种选择是在使用它的函数中导入 main:

main.py

import sys

def parseEvnt():
    import main
    sys.stdout.write("evnt:")
    main.parseCmd(1)     

如果您坚持不重构(这是真正的解决方案 - 不是一个聪明人),您可以将有问题的导入移动到 myParser.py

中的函数中
import sys

def parseEvnt():
    import main  ## import moved into function
    sys.stdout.write("evnt:")
    main.parseCmd(1)

再一次,看看您是否可以重新设计代码以避免这种相互依赖。

上述解决方案有点像 hack,不会解决您以后可能 运行 由于这种依赖性而遇到的问题。

只要模块在所有导入完成之前不尝试使用彼此的数据,您就可以经常摆脱循环依赖 - 实际上,这意味着引用名称space (from module import something 被禁止)并且只能在函数和方法中使用其他模块(全局 space 中没有 mystuff = module.mystuff)。那是因为导入开始时,python 将模块名称放入 sys.modules 并且不会再次尝试导入该模块。

您 运行 陷入麻烦,因为当您 运行 main.py 时,python 将 __main__ 添加到 sys.modules。当代码最终到达 import main 时,模块列表中没有 "main",因此再次导入 main.py...并且它的顶级代码试图 运行 .

让我们回顾运行你的测试用例并输入一些打印语句来判断导入何时发生。

myParser.py

print('  + importing myParser')
import sys

print('import parsecmd')
import parsecmd
def parseEvnt():
    sys.stdout.write("evnt:")
    parsecmd.parseCmd(1)

tbl.py

print('  + importing tbl')
print('import myParser')
import myParser
tblx = {
       1:("cmd",),
       2:("evnt",myParser.parseEvnt),
       }

Parsecmd.py(新)

print('  + importing parsecmd')
print('import tbl')
import tbl
def parseCmd(d):
    print(tbl.tblx[d][0])

main.py

print('running main.py')

print('import parsecmd')
import parsecmd

if __name__ == "__main__":
    data=[1,2]
    for d in data:
        if(d<2):
            parsecmd.parseCmd(d)
        else:
            fce = parsecmd.tbl.tblx[d][1]
            fce()    

当我 运行 我得到

running main.py
import parsecmd
  + importing parsecmd
import tbl
  + importing tbl
import myParser
  + importing myParser
import parsecmd                     <-- didn't reimport parsecmd
cmd
evnt:cmd