[英]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”,尚未完成執行。 經典循環導入。
所以這應該表明我的代碼結構很差,需要重新設計以避免循環依賴。
有什么選擇呢?
我能想到的一些解決方案,以及為什么我不想使用它們:
所以我的問題不僅僅是如何避免循環導入,而是以一種與我試圖實現的方式一樣干凈(如果不是更干凈)的方式。
有沒有人有更好的方法?
編輯
我已經對此進行了更徹底的研究,並得出結論,這是核心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.