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.py
和 myParser.py
都可以导入函数。
解决方案 2:让主通道 parseCmd
到 parseEvnt
。
首先,让 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
你好,除了加倍代码之外,我确实陷入了循环依赖,这是不可重构的。
我有这样的东西(只是更复杂):
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.py
和 myParser.py
都可以导入函数。
解决方案 2:让主通道 parseCmd
到 parseEvnt
。
首先,让 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