在開發python project時,通常都會以package分開各部件,貫徹"Separation of concerns"。可是如果對python modules的概念不清晰,各部件之間的import可以成為project的大問題。
以下會以case study的形式解釋python import的常見問題:
Python module 基本概念
Definition
- Module: 任何
*.py
- Package: a collection of modules
What's the difference between a Python module and a Python package?
Module vs. Package?
為方便起見,以下可能把module
和package
混合使用。
把directory變成package
建立一個__init__.py
檔,如:
.
└── some_package
├── some_module.py
└── __init__.py
然後可以import some_package.some_module
。
Import自己旁邊的.py
files
假設以下folder:
.
├── file_a.py
└── file_b.py
file_a.py
:
from file_b import hello
hello()
file_b.py
:
def hello():
print('Hello world!')
執行:
$ python file_a.py
Hello world!
一切如預期一樣。 from file_b import hello
其實用的是 Absolute import,在Python的權威--PEP 328提到:
import foo
...
it is proposed that foo will always be a module or package reachable from sys.path. This is called an absolute import.
所以由python 2.5開始,所有直接import xxx
都會是top-level module。在上述的case中,因為user是在current directory執行script,所以所有同level的files全都被當作top-level module,可以被直接import
。
把file_b.py
變成package
.
├── file_a.py
└── file_b
└── __init__.py
這樣file_b
就成為了一個package。
Absolute import的問題
Absolute import有一個很大的限制,就是所有module的名稱要unique,才不會有ambiguity。因為sys.path
裡的module不止你的project files,還有在 environment 中安裝了的所有library。
Relative import (Case 1)
於是當然是用relative import解決啊,不然要幹嘛?
把整個directory變成module:
.
├── __init__.py
├── file_a.py
└── file_b.py
使用relative import:
from .file_b import hello
然後......就Error了
# python3
Traceback (most recent call last):
File "file_a.py", line 1, in <module>
from .file_b import hello
SystemError: Parent module '' not loaded, cannot perform relative import
# python2
Traceback (most recent call last):
File "file_a.py", line 1, in <module>
from .file_b import hello
ValueError: Attempted relative import in non-package
因為from .file_b
== 從當前module中import另一個叫file_b
的submodule。
但執行python file_a.py
時,python是不會把current directory當作一個module看的。即是說__init__.py
在這裡並沒有甚麼卵用。
Relative import from parent (Case 2)
假設掉過頭來,你想在file_b
取用file_a
:
.
├── file_a.py
└── file_b
└── __init__.py
file_b/__init__.py
:
from ..file_a import hello # ".."代表parent directory
hello()
然後:
$ python file_b/__init__.py
一切好像很合理,可是還會error!
那__init__.py
要怎麼用呢?
正確的Relative import
然而並沒有正確的做法,因為以上兩個case都沒有錯!!!
只是根本不應該這樣run!Relative Import只能用在package裡面,而package裡面的scripts是不應該直接run的。
例如可以加一層parent:
.
├── main.py
└── some_package
├── __init__.py
├── file_a.py
└── file_b.py
main.py
:
from some_package import file_a
然後執行:
$ python main.py
hello world!
總結:
- Modules之間可以用、也應該要用relative import,來避免absolute import撞名的問題。
- 不可以直接run package裡面的script。
其他注意事項
避免module裡有definition以外的東西
在上面的例子中,只是在main.py
中import file_a
,就直接顯示了hello world!
。這是因為file_a.py
裡面直接執行了hello()
。
在寫一個modular的python project時,要盡量避免這些"Side effects",否則在使用modules時很容易出現各種bug。
修正
if __name__ == '__main__':
hello()
這樣file_a.py
只會在直接run的時候才會執行hello.py
(通常都是用來debug)。