簡體   English   中英

Python 3 模塊和包相對導入不起作用?

[英]Python 3 modules and package relative import doesn't work?

我在構建我的項目結構時遇到了一些困難。

這是我的項目目錄結構:

MusicDownloader/
   __init__.py
   main.py
   util.py
   chart/
      __init__.py
      chart_crawler.py
   test/
      __init__.py
      test_chart_crawler.py

這些是代碼:

1.main.py

from chart.chart_crawler import MelonChartCrawler

crawler = MelonChartCrawler()

2.test_chart_crawler.py

from ..chart.chart_crawler import MelonChartCrawler

def test_melon_chart_crawler():
  crawler = MelonChartCrawler()

3.chart_crawler.py

import sys
sys.path.append("/Users/Chois/Desktop/Programming/Project/WebScrape/MusicDownloader")
from .. import util

class MelonChartCrawler:
  def __init__(self):
    pass

4.util.py

def hi():
   print("hi")

在 MusicDownloader 中,當我通過python main.py執行 main.py 時,它顯示錯誤:

  File "main.py", line 1, in <module>
    from chart.chart_crawler import MelonChartCrawler
  File "/Users/Chois/Desktop/Programming/Project/WebScrape/MusicDownloader/chart/chart_crawler.py", line 4, in <module>
    from .. import util
ValueError: attempted relative import beyond top-level package

但是當我通過py.test test_chart_crawler.py在 test 目錄中執行我的測試代碼時,它可以工作

當我第一次面對絕對導入和相對導入時,這似乎非常簡單和直觀。 但它現在讓我發瘋。 需要你的幫助。 謝謝

第一個問題是MusicDownloader不是一個包。 添加__init__.pyMusicDownloader沿main.py和你的相對進口..chart應該工作。 相對導入僅在包內有效,因此您不能..到非包文件夾。

編輯我的帖子,為您的答案編輯提供更准確的答案。

這都是關於__name__ 相對導入使用__name__所在模塊的__name__from .(.)部分來形成要導入的完整包/模塊名稱。 用簡單的術語解釋進口商的__name__from部分連接,點顯示要忽略/刪除的名稱組件的數量,即:

__name__='packageA.packageB.moduleA'包含行的文件: from .moduleB import something ,導致 import packageA.packageB.moduleB組合值,所以大致from packageA.packageB.moduleB import something (但不是絕對導入,因為它如果直接輸入這樣的話)。

__name__='packageA.packageB.moduleA'包含行的文件: from ..moduleC import something ,導致 import packageA.moduleC組合值,所以大致from packageA.moduleC import something (但不是絕對導入,如果直接打字)。

這里是moduleB(C)還是moduleB(C) packageB(C)並不重要。 重要的是我們仍然有packageA部分,它在兩種情況下都可以作為相對導入的“錨點”。 如果沒有packageA部分,則不會解析相對導入,並且我們將收到類似“嘗試在頂級包之外進行相對導入”之類的錯誤。

這里還有一點要注意,當一個模塊運行時,它會得到一個特殊的__name____main__ ,這顯然會阻止它解決任何相關的導入。

現在關於您的情況,請嘗試將print(__name__)添加為每個文件的第一行,並在不同場景中運行您的文件並查看輸出如何變化。

也就是說,如果你直接運行你的 main.py,你會得到:

__main__
chart.chart_crawler
Traceback (most recent call last):
  File "D:\MusicDownloader\main.py", line 2, in <module>
    from chart.chart_crawler import MelonChartCrawler
  File "D:\MusicDownloader\chart\chart_crawler.py", line 2, in <module>
    from .. import util
ValueError: Attempted relative import beyond toplevel package

這里發生的事情是...... main.py不知道MusicDownloader是一個包(即使在之前添加了__init__.py編輯之后)。 在您的chart_crawler.py__name__='chart.chart_crawler'並且當使用from ..運行相對導入時,包的組合值將需要刪除兩部分(每個點一個),如上所述,因此結果將變為''因為只有兩部分,沒有封閉的包裹。 這導致異常。

當你導入一個模塊時,它里面的代碼會運行,所以它和執行它幾乎一樣,但沒有__name__變成__main__和封閉的包,如果有的話,被“注意到”。

因此,解決方案是將main.py作為MusicDownloader包的一部分導入。 要完成上述操作,請使用以下代碼在與MusicDownloader文件夾(靠近它,而不是靠近main.py內部)相同的層次結構級別上創建一個名為launcher.py的模塊:

print(__name__)
from MusicDownloader import main

現在運行launcher.py並查看更改。 輸出:

__main__
MusicDownloader.main
MusicDownloader.chart.chart_crawler
MusicDownloader.util

這里__main__launcher.py__name__ chart_crawler.py__name__='MusicDownloader.chart.chart_crawler'並且當使用from ..運行相對導入時, package 的組合值將需要刪除兩部分(每個點一個),如上所述,因此結果將變為'MusicDownloader'與 import 變成from MusicDownloader import util 正如我們在下一行看到的,當util.py成功導入時,它會打印其__name__='MusicDownloader.util'

所以__name__就是這樣 - “這都是關於__name__ ”。

PS 沒有提到的一件事是為什么帶有test包的部分有效。 它沒有以常見的方式啟動,您使用了一些額外的模塊/程序來啟動它,並且它可能以某種方式導入它,所以它起作用了。 要理解這一點,最好看看該程序是如何工作的。

官方文檔中有一個注釋:

請注意,相對導入基於當前模塊的名稱。 由於主模塊的名稱始終為“__main__”,因此用作 Python 應用程序主模塊的模塊必須始終使用絕對導入。

暫無
暫無

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

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