python - 如何避免循环导入

标签 python python-import

我是 Python 新手,之前我一直在使用 Swift 之类的语言,其中导入并不是什么大问题:你只是定义了一个新类,并且可以从程序的另一部分访问它。

我不能在 Python 中使用这种方式,因为这里的 import 以另一种方式工作:你不能进行循环导入,两个文件相互导入。我知道我正面临这个问题,因为以错误的方式使用语言,但我不明白如何避免它。

我的意思是,在大多数情况下,您可以通过将两个类组合到一个文件中来解决这个问题,但感觉不对。此外,我还找到了诸如“将导入语句移动到文件末尾”之类的建议,但这也不是一个好主意。

如果您愿意,我想了解 Python 的哲学。在决定在单独的文件中创建类(class)时,我应该如何组织我的项目以及我应该遵循什么指导?

最佳答案

你当然可以导入child来自 parentparent来自 child .完成这项工作的关键是 child不要深入探究parent从它的模块级代码,因为parent在 Python 运行 child 中的模块级代码时,模块仅部分加载.

以下是导入 child 时发生的情况来自 parentparent来自 child (假设 parent 首先加载):

  • parent 模块级别的代码运行直到它到达加载 child 的语句(import childfrom child import something )。 “模块级别”是指不在类或函数定义中的语句。在模块级别定义的类和函数也将被创建为模块内的对象。但是,函数和类方法本身还不会运行。
  • 当 Python 到达 import child parent 中的声明(或等价物)的模块级代码,它将停止运行 parent代码并开始运行 child 中的模块级代码.如果 child进口 parent通过 import parentfrom parent import something ,它将得到 parent模块处于当前的部分构建状态。所以 child 中的模块级代码无法访问下面定义的对象 import childparent.py .
  • 一次child的模块级代码运行完毕,控制权将返回parent低于import child语句,Python 将完成运行 parent 中的所有模块级代码.

  • 如果child,这个过程会给你带来麻烦。的模块级代码尝试访问 parent 中定义的对象。在 import child 之后语句(因为 parent 仅在 child 加载时部分完成)。解决方法是导入 parent在模块级别 child但推迟访问 parent 内的对象来自 child直到 child 之后和 parent已完成加载。特别是,不要使用 from parent import somethingchild的模块级代码,您可能需要使用 import parent ,然后访问 parent.somethingchild 中的函数或方法代码内部.这样做是安全的,因为这些函数和方法要到 child 之后才会运行。和 parent完成运行,此时 parent 的所有元素模块定义正确。

    这是我的意思的一个例子,使用你在评论中描述的设置。如果您提供有关给您带来问题的代码的更多信息,我可以更紧密地调整它。

    版本 1(不起作用)

    __main__.py:
    from user import User
    u = User()
    

    用户.py:
    from data_manager import DataManager
    ...
    class User:
        def __init__(self, data_manager=None):
            if data_manager is None:
                data_manager = DataManager(user=self)
            self.data_manager = data_manager
    

    数据管理器.py:
    # next line will fail because user.py has been partly loaded
    # and user.User doesn't exist yet
    from user import User
    ...
    class DataManager:
        def __init__(self, user=None):
            ...
            if user is None:
                user = User(data_manager=self)
            self.user = user
    

    版本 2(将工作)

    __main__.py:
    from user import User
    u = User()
    

    用户.py:
    import data_manager as dm
    ...
    class User:
        def __init__(self, data_manager=None):
            if data_manager is None:
                data_manager = dm.DataManager(user=self)
            self.data_manager = data_manager
    

    数据管理器.py:
    import user as user_module
    ...
    # this defines a class which will eventually create an instance
    # of user.User, but it won't try to do that until control returns
    # to __main__.py, by which point the `user` and `data_manager`
    # modules are fully defined
    class DataManager:
        def __init__(self, user=None):
            ...
            if user is None:
                user = user_module.User(data_manager=self)
            self.user = user
    

    请注意,您的类(class)中的引用 __init__在实际实例化类之前,方法不会得到解决。即 user = user_module.User(data_manager=self)行执行如下操作:“在当前模块中查找名为 user_module 的对象;在该对象中查找 User 属性;构造该类的对象”。重要的是 data_manager可以安全导入user早期的模块(模块已经存在,即使它只是部分构建),但上面的代码实际上不会在 user 中查找任何内容模块直到 DataManager对象被实例化,此时 user.User将被正确定义。

    关于python - 如何避免循环导入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43973509/

    相关文章:

    python - 在pygame中临时保存图像

    Python 模块 : how to prevent importing modules called by the new module

    Python:如何从兄弟文件夹添加模块

    python - 错误索引错误加载文件配置

    python - 如何在 Python 中表示无限数?

    python - 测试期间Django双重导入

    python - Python继承检测如何工作?

    python - 无法使用 Celery + Django 在 tasks.py 中导入模型

    python - Python 中有类似 "void lambda"的东西吗?

    python - Pandas:同时导入日期和 12 小时时间