簡體   English   中英

Python 3.5“ ImportError:無法導入名稱'SomeName'

[英]Python 3.5 "ImportError: cannot import name 'SomeName'

我正在嘗試為Python 3.5實現一個小型庫,但一直在努力如何正確處理包/模塊的結構以及如何使導入工作。

我一直遇到python抱怨無法導入某些名稱的錯誤,例如

ImportError: cannot import name 'SubClass1'

當“ SubClass1”需要導入其他模塊但該其他模塊也需要了解SubClass1(循環導入)時,似乎會發生這種情況。 我需要在庫中進行循環導入,因為基類具有創建適當子類實例的工廠方法(在其他情況下,也需要循環導入,例如,檢查函數參數的類型時,需要導入該類型所在的位置定義,但是該模塊本身可能需要完成檢查的類:另一個循環依賴項!)

這是示例代碼:

根目錄包含子目錄dir1。 目錄dir1包含一個空文件init .py,一個文件baseclass.py和一個文件subclass1.py。 文件./dir1/subclass1.py包含:

from . baseclass import BaseClass
class SubClass1(BaseClass):
   pass

文件./dir1/baseclass.py包含:

from . subclass1 import SubClass1
class BaseClass(object):
   def make(self,somearg):
      # .. some logic to decide which subclass to create
      ret = SubClass1()
      # .. which gets eventually returned by this factory method
      return ret

文件./test1.py包含:

from dir1.subclass1 import SubClass1
sc1 = SubClass1()

這將導致以下錯誤:

Traceback (most recent call last):
  File "test1.py", line 1, in <module>
     from dir1.subclass1 import SubClass1
  File "/data/johann/tmp/python1/dir1/subclass1.py", line 1, in <module>
    from . baseclass import BaseClass
  File "/data/johann/tmp/python1/dir1/baseclass.py", line 1, in <module>
    from . subclass1 import SubClass1
ImportError: cannot import name 'SubClass1'

解決此問題的標准/最佳方法是什么,理想情況下是向后兼容python 2.x和python 3直至3.2版本的方法?

我在其他地方讀過,導入模塊而不是從模塊中導入內容可能會有所幫助,但是我不知道如何以相對的方式僅導入模塊(例如subclass1),因為“ import。subclass1”或類似方法不起作用。

您的問題是由循環導入引起的。 baseclass模塊正嘗試從subclass1模塊導入SubClass1 ,但是subclass正嘗試向后導入BaseClass 您會收到NameError因為在運行import語句NameError未定義類。

有幾種方法可以解決此問題。

一種選擇是更改導入樣式。 無需按名稱導入類,只需導入模塊並稍后將名稱作為屬性查找。

from . import baseclass

class SubClass1(baseclass.BaseClass):
    pass

和:

from . import subclass1

class BaseClass:
    def make(self,somearg):
        # ...
        ret = subclass1.SubClass1()

因為SubClass1需要能夠在定義時立即使用BaseClass ,所以如果在subclass1之前導入baseclass模塊,則此代碼可能仍然會失敗。 所以不理想

另一種選擇是更改baseclass以使其在BaseClass的定義之下進行導入。 這樣subclass模塊將能夠在需要時導入名稱:

class BaseClass:
    def make(self,somearg):
        # .. some logic to decide which subclass to create
        ret = SubClass1()

from .subclass1 import SubClass1

這是不理想的,因為通常將導入放置在文件的頂部。 將它們放置在其他位置會使代碼更加混亂。 您可能想在文件頂部加一個注釋,以解釋為什么走這條路線為什么要延遲導入。

另一個選擇是將兩個模塊合並為一個文件。 Python不需要像其他語言一樣,每個類都具有自己的模塊。 當您擁有緊密耦合的類(例如您的示例中的類)時,將它們全部放在一個地方很有意義。 這樣就可以避免整個問題,因為根本不需要任何導入。

最后,還有一些更復雜的解決方案,例如依賴注入。 而不是基類不需要了解子類,每個子類都可以通過調用某些函數並將引用傳遞給自身來注冊自己。 例如:

# no imports of subclasses!

def BaseClass:
    subclasses = []

    def make(self, somearg):
        for sub in self.subclasses:
            if sub.accepts(somearg):
                return sub()
        raise ValueError("no subclass accepts value {!r}".format(somearg))

    @classmethod
    def register(cls, sub):
        cls.subclasses.append(sub)
        return sub        # return the class so it can be used as a decorator!

並在subclass.py

from .baseclass import BaseClass

@BaseClass.register
class SubClass1(BaseClass):
    @classmethod
    def accepts(cls, somearg):
        # put logic for picking this subclass here!
        return True

這種編程風格稍微復雜一些,但是它很好,因為它比BaseClass需要預先了解所有子類的版本更容易擴展。 您可以通過多種方式來實現這種樣式的代碼,使用register功能只是其中之一。 關於它的一件好事是它並不嚴格要求繼承(因此,您可以注冊一個實際上並不從BaseClass繼承的類)。 如果僅處理實際的繼承子類,則可能要考慮使用一個元類,該元類會自動為您完成所有子類的注冊。

暫無
暫無

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

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