簡體   English   中英

包內進口並不總是有效

[英]Intra-package imports do not always work

我有一個像這樣結構的Django項目:

appname/
   models/
      __init__.py
      a.py
      base.py
      c.py

... appname / models / __ init__.py只包含這樣的語句:

from appname.models.base import Base
from appname.models.a import A
from appname.models.c import C

...以及appname / models / base.py包含的位置:

import django.db.models


class Base(django.db.models.Model):
   ...

以及appname / models / a.py包含的位置:

import appname.models as models


class A(models.Base):
   ....

...同樣適用於appname / models / c.py等。

我很滿意我的代碼結構,但當然它不起作用,因為循環導入。

當appname / __ init__.py運行時,appname / models / a.py將運行,但該模塊導入“appname.models”,尚未完成執行。 經典循環導入。

所以這應該表明我的代碼結構很差,需要重新設計以避免循環依賴。

有什么選擇呢?

我能想到的一些解決方案,以及為什么我不想使用它們:

  1. 將我的所有模型代碼合並到一個文件中:在我看來,在同一個文件中有20多個類比我嘗試做的(使用單獨的文件)更糟糕。
  2. 將“Base”模型類移動到“appname / models”之外的另一個包中:這意味着我最終會在我的項目中包含包含基類/父類的包,這些類理想情況下應該拆分為其子/子包中的包課程位於。 為什么我應該在同一個包中有模型,表單,視圖等的基類/父類,而不是它們自己的包(子/子類所在的位置),除了避免循環導入?

所以我的問題不僅僅是如何避免循環導入,而是以一種與我試圖實現的方式一樣干凈(如果不是更干凈)的方式。

有沒有人有更好的方法?

編輯

我已經對此進行了更徹底的研究,並得出結論,這是核心Python或Python文檔中的錯誤。 此問題和答案提供更多信息。

Python的PEP 8表明絕對優先於絕對超過相對進口。 此問題有一個涉及相對導入的解決方法,並且導入機制中可能存在修復。

我在下面的原始答案提供了示例和解決方法。

原始答案

正如您所正確推斷的那樣,問題是循環依賴。 在某些情況下,Python可以很好地處理這些,但是如果你得到太多的嵌套導入,它就會出現問題。

例如,如果你只有一個包級別,實際上很難讓它破壞(沒有相互導入),但是一旦你嵌套包,它就更像是相互導入,並且開始變得難以制作這行得通。 這是一個引發錯誤的示例:

level1/__init__.py

    from level1.level2 import Base

level1/level2/__init__.py

    from level1.level2.base import Base
    from level1.level2.a import A

level1/level2/a.py

    import level1.level2.base
    class A(level1.level2.base.Base): pass

level1/level2/base

    class Base: pass

錯誤可以通過幾種不同的方式“修復”(對於這個小案例),但許多潛在的修復都很脆弱。 例如,如果您不需要在level2 __init__文件中導入A ,則刪除該導入將解決問題(並且您的程序稍后可以執行import level1.level2.aA ),但如果您的包變得更復雜,那么將再次看到錯誤蔓延。

Python有時可以很好地使這些復雜的導入工作,並且它們何時能夠工作和不工作的規則根本不直觀。 一般規則是from xxx.yyy import zzz可以比import xxx.yyy更加寬容,然后是xxx.yyy.zzz 在后一種情況下,解釋器必須在檢索xxx.yyy.zzz時完成將yyy綁定到xxx命名空間,但在前一種情況下,解釋器可以在頂層包之前遍歷包中的模塊命名空間已完全設置。

所以對於這個例子,真正的問題是a.py的裸導入這很容易修復:

    from level1.level2.base import Base
    class A(Base): pass

始終使用相對導入是強制使用from ... import一種好方法,原因很簡單,相對導入在沒有from'. To use relative imports with the example above,情況下不起作用from'. To use relative imports with the example above, from'. To use relative imports with the example above, level1 / level2 / a.py`應包含:

from .base import Base
class A(Base): pass

這打破了有問題的導入周期,其他一切正常。 如果導入的名稱(例如Base)在沒有以源模塊名稱作為前綴時過於混亂,則可以在導入時輕松地重命名:

from .base import Base as BaseModel
class A(BaseModel): pass

雖然這解決了當前的問題,但如果包結構變得更復雜,您可能需要考慮更普遍地使用相對導入。 例如, level1/level2/__init__.py可以是:

from .base import Base
from .a import A

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM