如何避免循环导入

时间:2017-05-15 07:17:42

标签: python python-import

我是Python的新手,之前我一直在使用像Swift这样的语言,导入并不是什么大不了的事:你只是定义一个新类,并且可以从另一个部分访问它。你的计划。

我无法在Python中使用这种方式,因为此处的导入工作方式是另一种方式:您无法进行循环导入,其中两个文件相互导入。我知道我因为错误地使用语言而面临这个问题,但我不明白如何避免这种情况。

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

如果愿意,我想了解Python的理念。在决定在单独的文件中创建课程时,我应该如何组织我的项目以及我应该引导什么?

3 个答案:

答案 0 :(得分:0)

您当然可以从childparent parent导出child。实现这项工作的关键是child不要从模块级代码中深入探讨parent,因为parent模块仅在Python运行时被部分加载child中的模块级代码。

以下从child parentparent child导入parent时会发生什么情况(假设首先加载parent):

  1. 模块级别child的代码会一直运行,直到它到达加载import childfrom child import somethingimport child)的语句。通过"模块级别",我的意思是不在类或函数定义中的语句。在模块级别定义的类和函数也将作为模块中的对象创建。但是,函数和类方法本身不会运行。
  2. 当Python到达parent模块级代码中的parent语句(或等效语句)时,它将停止运行child代码并开始运行模块child中的级别代码。如果parent通过import parentfrom parent import something导入parent,则会使child模块处于当前的部分构建状态。因此,import child中的模块级代码无法访问parent.pychild下面定义的对象。
  3. 一旦parent的模块级代码完成运行,控件将返回import child语句下方的parent,Python将完成所有模块级别的运行child中的代码。
  4. 如果parent的模块级代码尝试访问import child语句之后parent中定义的对象,则此过程会给您带来麻烦(自{{1仅child加载时才会部分完成。解决方法是在parent的模块级别导入child,但是将parent中的对象从child推迟到child和{{1}之后已完成加载。特别是,您可能需要使用parent,然后从内部函数或方法中访问from parent import something,而不是在child的模块级代码中使用import parent parent.something中的代码。这样做是安全的,因为这些函数和方法在childchild完成运行之后才会运行,此时parent模块的所有元素都被正确定义

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

    版本1(赢了工作)

    __主__ PY:

    parent

    user.py:

    from user import User
    u = User()
    

    data_manager.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
    

    版本2(可以使用)

    __主__ 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
    

    user.py:

    from user import User
    u = User()
    

    data_manager.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
    

    请注意您班级中的参考资料'在实际实例化类之前,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 方法无法解决。即,__init__行的行为如下:"在当前模块中查找名为user = user_module.User(data_manager=self)的对象;在该对象中查找user_module属性;构建该类的对象"。重要的是User可以在早期安全地导入data_manager模块(模块已经存在,即使它只是部分构建),但上面的代码实际上并没有找到任何东西在user模块内部,直到user对象被实例化,到时DataManager将被正确定义。

答案 1 :(得分:-1)

  

我不能以这种方式使用Python,因为这里的导入工作方式是另一种方式:你不能进行循环导入,其中两个文件相互导入。

是的,你可以!

假设我们在同一个工作目录中有两个文件:

#file1.py
import file2
x = 22
y = 'spam'
print(x)
print(y)

#file2.py
import file1
print("file1")

请注意我们导入 file1.py

时会发生什么
>>> import file1
file1
22
spam

file1.py 导入 file2.py file2.py 导入 file1.py

答案 2 :(得分:-2)

发生此问题是因为您正在编写Python程序,就像编写Swift或Java程序一样。这种方法从来都不行:每种语言都不同,并且有不同的最佳实践。如果你编写unpythonic代码,不仅它看起来很丑陋而且对其他Python开发人员来说难以理解,那么你就会挣扎于语言而不是享受它。

只需像python开发人员那样构建代码。在一个模块中将相关类分组。如果罕见的情况是难以避免导入周期(几乎从未发生过),请在函数或方法中导入违规依赖,而不是全局范围。