循环依赖 - 最佳解决方案?

0 投票
1 回答
1989 浏览
提问于 2025-04-18 10:10

我有三个文件:a.pyb.pyc.py

这三个文件在不同的文件夹里(我们称它们为a_dirb_dirc_dir)。最开始我只有两个文件a.pyb.pyb.py的内容是这样的:

from root_dir.a_dir import a
...
do something
...

这样一切都正常。但当我创建了第三个文件c.py,并像这样在a.py中引入它:

from root_dir.c_dir import c
...
do something
... 

我开始收到错误提示,Python无法在b.py中导入a。所以我想这可能是循环依赖的问题?然后我改变了a.py中的导入方式,像这样:

def method_with_c_import(s):
    from root_dir.c_dir import c
    ...
    do something inside method
    ...

所以我只在需要的地方使用了导入(我在其他地方不需要这个导入,而且那个方法在b.py中没有被使用)。

但这样做是最好的方法吗?或者有没有更好的解决方案?

根据要求的错误追踪(如果我把在a.py中添加的导入放在文件开头的方法外面,就会出现这个错误):

Traceback (most recent call last):
  File "/home/oerp/openerp70/openerp/server/openerp/cli/server.py", line 97, in preload_registry
    db, registry = openerp.pooler.get_db_and_pool(dbname,update_module=update_module)
  File "/home/oerp/openerp70/openerp/server/openerp/pooler.py", line 33, in get_db_and_pool
    registry = RegistryManager.get(db_name, force_demo, status, update_module)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/registry.py", line 203, in get
    update_module)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/registry.py", line 233, in new
    openerp.modules.load_modules(registry.db, force_demo, status, update_module)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/loading.py", line 350, in load_modules
    force, status, report, loaded_modules, update_module)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/loading.py", line 256, in load_marked_modules
    loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules, perform_checks=perform_checks)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/loading.py", line 159, in load_module_graph
    load_openerp_module(package.name)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/module.py", line 415, in load_openerp_module
    getattr(sys.modules['openerp.addons.' + module_name], info['post_load'])()
  File "/home/oerp/openerp70/openerp/server/openerp/addons/web/http.py", line 628, in wsgi_postload
    openerp.wsgi.register_wsgi_handler(Root())
  File "/home/oerp/openerp70/openerp/server/openerp/addons/web/http.py", line 517, in __init__
    self.load_addons()
  File "/home/oerp/openerp70/openerp/server/openerp/addons/web/http.py", line 580, in load_addons
    m = __import__('openerp.addons.' + module)
  File "/home/oerp/openerp70/openerp/server/openerp/modules/module.py", line 133, in load_module
    mod = imp.load_module('openerp.addons.' + module_part, f, path, descr)
  File "/home/oerp/openerp70/openerp/addons/ambulance_system/__init__.py", line 24, in <module>
    import report
  File "/home/oerp/openerp70/openerp/addons/ambulance_system/report/__init__.py", line 15, in <module>
    import marketing_clinic_report
  File "/home/oerp/openerp70/openerp/addons/ambulance_system/report/marketing_clinic_report.py", line 6, in <module>
    from ambulance_system.model import new_medical_card as nmc
ImportError: cannot import name new_medical_card

附注:这个错误是针对我在问题中提到的文件b.py(当我按照上面所写的在a.py中进行更改后出现的错误追踪)。

更新 目录的优先导入:

import generic #c.py directory
import model #a.py directory
import report #b.py directory

更新2

根据要求提供一个示例以重现问题(new_medical_card.pya.pymarketing_clinic_report.pyb.pygeneric.pyc.py):

模块文件和目录结构

ambulance_system/:
  - generic/generic.py
  - model/new_medical_card.py
  - report/marketing_clinic_report.py

三个文件中的导入:

generic.py

import math

new_medical_card.py

from openerp.osv import osv, orm, fields
import tools
from tools.translate import _
import time
from datetime import date, datetime, timedelta
ambulance_system.model.generic import generic as grc #This one produces error

marketing_clinic_report.py

import time
from report import report_sxw
from ambulance_system.model import new_medical_card as nmc

根据要求更新3 初始化导入:

我已经展示了主要的初始化文件(根目录中的那个 - ambulance_system,但为了清晰起见,这里也会再贴一次):

__init__.py 目录的优先导入:

import generic #c.py directory
import model #a.py directory
import report #b.py directory

__init__.py用于generic目录(c.py):

import generic

__init__.py用于model目录(a.py):

import new_medical_card
import medical_card_segment_res
import medical_card
import res_partner
import model_request_access
import new_medical_card_requirement
import medical_card_rule
import hr_department_team

__init__.py用于report目录(b.py):

import card_field_report
import medical_card_field_report
import medical_card_reject_report
import new_medical_card_reject_report
import new_medical_card_field_report
import old_diagnosis_report
import new_diagnosis_report
import old_form_ambulance_usage_report
import new_form_ambulance_usage_report
import medical_card_history
import new_medical_card_history
import new_medical_card_report
import report1
import report2
import marketing_clinic_report
import print_gsc_data

1 个回答

3

这样的方法是最好的解决方案吗?或者说有没有更好的办法来解决这个问题呢?

不,这不是最好的方法!最好的方法是:

消除循环依赖

真的。这意味着你的模块设计有问题。循环依赖本来就不应该存在。实际上,很多编程语言根本不允许出现这种情况。

你有几种方法可以解决这个问题:

  • 调整这三个模块之间的定义,让每个模块不需要导入其他两个模块。
  • 调整定义,使得导入只朝一个方向进行:比如说 b 导入 a,而 c 导入 ab
  • 删除一个模块。你可能决定把一些本该放在一起的东西拆开了。例如,c 可能并不需要,最好是不要拆分 a
  • 拆分某个模块(比如 c),根据依赖关系将其定义分组。例如,c_a 将包含所有依赖于 ac 的定义,而 c_b 则包含所有依赖于 b 的定义,并且 c_ac_b 之间不相互导入
  • 彻底重写你正在做的设计。(循环依赖可能是更深层次问题的表现...)

如果出于某种原因,你确实想保留循环依赖,你可能可以通过把导致依赖的 import 放在文件的末尾来消除错误,而不是放在顶部。

不过,这并不总是可行,但如果你只在函数定义内部使用这个模块,这个方法应该有效。

另外,这样做会失去 import 的分组,这样你就不能简单地通过查看模块顶部来理解模块的依赖关系... 但如果导入在函数内部,这种情况会更糟,因为你还需要查看多个地方

撰写回答