你經常看到卻經常忽視的__init__.py有什麼用?

  • 2020 年 1 月 23 日
  • 筆記

Python 初學者在閱讀一些 Python 開源項目時,常常會看到一個叫做__init__.py的文件。下圖為著名的第三方庫requests的源程式碼:

那麼__init__.py有什麼用呢?本文介紹它的兩個用途。

精簡導入路徑

假設我們有兩個文件main.py與writer.py,他們所在的文件結構如下圖所示:

main.py在DocParser文件夾下面;writer.py在DocParser/microsoft/docx/文件夾下面。writer.py文件中有一個函數write。其內容如下:

現在我想在main.py文件夾調用這個函數,那麼我有3種寫法:

from microsoft.word.docx.writer import write    write()

運行效果如下圖所示:

或者這樣寫:

from microsoft.word.docx import writer    writer.write()

運行效果如下圖所示:

或者這樣寫:

import microsoft.word.docx.writer    microsoft.word.docx.writer.write()

運行效果如下圖所示:

但無論那種方法,都要寫長長的路徑,甚為不便。這個時候,我們可以在microsoft文件夾下面創建一個__init__.py文件,並在裡面填寫如下內容:

此時,在 main.py,我們導入write函數,就像是從microsoft.py 文件中導入一樣。如下圖所示:

注意上面我說的是就像從microsoft.py 文件中導入一樣,這裡是用的文件而不是文件夾。

這是因為,當一個文件夾裡面有__init__.py以後,這個文件夾就會被 Python 作為一個包(package)來處理。此時,對於這個包裡面層級比較深的函數、常量、類,我們可以先把它們導入到__init__.py中。這樣以來,包外面再想導入這些內容時,就可以用from 包名 import 函數名來導入了。

無視工作區的相對引用

為什麼會有包這個東西呢?這是因為,當有一些程式碼會在很多地方被使用時,我們可以把這些程式碼打包起來,作為一個公共的部分提供給其他模組調用。

由於調用包的其他模組所在的絕對路徑是千變萬化的,所以在包的內部調用自身其他文件中的函數、常量、類,就應該使用相對路徑,而不是絕對路徑。

例如,現在我在microsoft文件夾下面再創建一個parse.py文件,它需要導入microsoft/word/docx/writer.py中的write函數,那麼程式碼可以寫為:

from .word.docx.writer import write      def parse_doc():      print('現在在 parse.py 中調用 write 函數')      write()

如下圖所示:

可以看到,此時from .word.docx.writer中的.表示當前包,所以.word.docx.writer表示當前包下面的word 文件夾下面的 docx 文件夾下面的 writer.py 文件。

然後我們修改__init__.py,把parse_doc函數導入進去,如下圖所示:

最後,在main.py中調用這個parse_doc函數,如下圖所示:

可以看到,在包裡面的一個文件調用這個包裡面的另一個文件,只需要知道另一個文件的相對位置就可以了,不用關心這個包被放在哪裡。

並且,上面parse.py中導入write函數的程式碼還可以進一步簡化,由於write函數已經被導入到了__init__.py中,所以我們可以直接從.裡面導入write函數:

from . import write

運行效果如下圖所示:

總結

當一個文件夾裡面包含__init__.py時,這個文件夾會被 Python 認為是一個包(package),此時,包內部的文件之間互相導入可以使用相對導入,並且通過提前把函數、常量、類導入到__init__.py中再在其他文件中導入,可以精簡程式碼。

Tags: