Python/Django: 为什么在使用前导入模块会阻止循环导入?

Python/Django: Why does importing a module right before using it prevent a circular import?

我已经 运行 在不同的情况下多次遇到这个问题,但我的设置如下:

我有两个 Django 模型文件。一个包含用户模型和优惠券代码,用户可以使用它们来注册课程。这些都在 account/models.py 文件中。 Course 和相关的多对多字段位于不同的模型文件 course/models.py 中。我通常在我的代码中将它们分别称为 amod 和 cmod。

在 course/models.py 中我有一个导入语句:

from account import models as amod

class Course(ExtendedModel):
    stuff = stuff

我需要为此处未显示的课程和用户之间的多对多 model/table 导入 account/models.py 文件。到目前为止,还不错。

在 account/models.py 文件中我有 CouponCode 模型。每个实例都被创建,然后可以在创建后分配给特定的课程对象,以允许学生使用它来注册系统中的课程。

class CouponCode(ExtendedModel):
    assigned_course = UniqueIDForeignKey("course.Course", blank=True, null=True, related_name='assigned_coupon_code_set')
    ...
    ...

    @staticmethod
    def assign_batch(c, upper_limit):
        import course.models as cmod # Why is this allowed here?
        assert isinstance(c, cmod.Course)

        # Do other stuff here

那个静态方法允许我传入一个课程对象和一些我想分配给它的 CouponCodes,然后它将下一个 N 个未分配的代码分配给该课程。我的问题来自 assert 语句。

我需要从 course/models.py 导入 Course 对象,以确保传入的对象实际上是 Course 的一个实例,但如果我在文件顶部这样做,我遇到问题,因为这个文件已经被导入 course/models.py。 (amod 正在导入到 cmod 中,然后在 amod 中我需要导入 cmod)。

如果我在需要它之前在方法中导入它而不是在文件顶部导入它,为什么允许我这样做?

当一个模块被导入时(好吧,它是在给定进程中第一次导入),所有的顶级语句都会被执行(记住import 一个可执行语句)。所以你不能让 module1 在顶层有一个 import module2 语句,而 module2 在顶层有一个 import module1 - 它显然不能工作。

现在,如果在模块 2 中将 import module1 语句移动到函数中,则该语句不会在函数实际调用之前执行,因此不会阻止模块 1 导入模块 2。

请注意,这仍然被认为是不好的做法,大多数时候循环依赖意味着你应该重构你的代码来避免这个问题(将两个模块依赖的部分提取到第三个模块中,该模块依赖于两个但是 none 的其他依赖,或简单地合并模块) - 但有些情况由于其他限制而变得复杂,因此将其作为最后的解决方案是可以的。

此外,您 不需要 导入模型以在 ForeignKeyMany2Many 字段中引用它 - 您可以传递 "appname.ModelName" 字符串,cf https://docs.djangoproject.com/en/1.8/ref/models/fields/#foreignkey

To refer to models defined in another application, you can explicitly specify a model with the full application label. For example, if the Manufacturer model above is defined in another application called production, you’d need to use:

class Car(models.Model):
    manufacturer = models.ForeignKey('production.Manufacturer')

This sort of reference can be useful when resolving circular import dependencies between two applications.