python 是通過module組織代碼的芯勘,每一個module就是一個python文件,但是modules是通過package來組織的腺逛。
如果我們自己寫著玩荷愕,有的時候就是一兩個Python文件在同級目錄下,但是當我們開始嘗試開發(fā)更為復雜的項目的時候棍矛,package這個概念的使用就有助于我們組織我們寫的一個個modules安疗。
module的概念相對簡單,所以不會再多說够委,主要是說一下package荐类。
Python package
package的定義很簡單,在當面目錄下有__init__.py
文件的目錄即為一個package茁帽。
但是這會分為兩種情況玉罐,第一種情況是一個空的__init__.py
文件,另外一個情況是寫了代碼的__init__.py
文件潘拨。不管是空的還是有內容的吊输,這個目錄都會被認為是一個package,這是一個標識。
package的初始化工作
一個package 被導入铁追,不管在什么時候__init__.py
的代碼都只會被執(zhí)行一次
>>> import package
hello world
>>> import package
>>> import package
由于 package 被導入時 __init__.py
中的可執(zhí)行代碼會被執(zhí)行季蚂,所以小心在 package 中放置你的代碼,盡可能消除它們產生的副作用琅束,比如把代碼盡可能的進行封裝成函數或類扭屁。
__init__.py
內的導入順序
當我嘗試導入
from package import something
import
語句會首先檢查something
是不是__init__.py
的變量,然后檢查是不是subpackage
疯搅,再檢查是不是module
,最后拋出ImportError
所以檢查順序如下:
-
__init__.py
文件內變量 - 是不是package內的
subpackage
- 是不是package內的
module
看個例子
我們有一個如下結構的package
在a.py
文件內有一個函數
def bar():
print("Hello, function 'bar' from module 'a' calling")
在b.py
文件內有一個函數
def foo():
print("Hello, function 'foo' from module 'b' calling")
然后我們添加一個空的__init__.py
文件在simple_package
里面幔欧。
我們看下,當我們import simple_package
的時候到底會發(fā)生什么事情(在simple_package
內激活Python shell
或者simple_package
的的路徑被包含在python
的sys.path
或者在PYTHONPATH
的環(huán)境變量中)
>>> import simple_package
>>>
>>> simple_package
<module 'simple_package' from '/home/bernd/Dropbox (Bodenseo)/websites/python-course.eu/examples/simple_package/__init__.py'>
>>>
>>> simple_package.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>>
>>> simple_package.b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
我們可以看到simple_package
已經被成功導入礁蔗,但是a.py
和b.py
并沒有被導入
當然了雁社,如果你希望使用import simple_package
后自動加載a
或者b
模塊,這里有兩種辦法霉撵。
第一種就是在__init__.py
內導入a
或者b
模塊洪囤,然后保存再激活python
的交互環(huán)境
#__init__.py
import a
import b
當你再次嘗試import simple_package
后,就可以使用simple_package.a.bar()
來使用模塊a
中的bar()
函數了瘤缩。
第二辦法就是手動導入,當你想使用模塊a
中的bar()
函數時伦泥,需要手動導入
import simple_package.a as a
然后就是可以a.bar()
來使用bar()
函數了。
一個更復雜的例子
這是一個來自官方的例子
文件結構如下
sound
|-- effects
| |-- echo.py
| |-- __init__.py
| |-- reverse.py
| `-- surround.py
|-- filters
| |-- equalizer.py
| |-- __init__.py
| |-- karaoke.py
| `-- vocoder.py
|-- formats
| |-- aiffread.py
| |-- aiffwrite.py
| |-- auread.py
| |-- auwrite.py
| |-- __init__.py
| |-- wavread.py
| `-- wavwrite.py
`-- __init__.py
你可以將這個package的例子下載下來府怯。如果直接使用import sound
來導入這個package,我們可以導入package sound
防楷,但是sound
的子package(effects
,filters
,formats
)并不會被自動導入牺丙。子package不會被自動導入的原因是因為在sound
目錄下的__init__.py
文件并沒有任何關于導入子package的代碼。
我們來看下在sound
目錄下的__init__.py
的代碼
"""An empty sound package
This is the sound package, providing hardly anything!"""
print("sound package is getting imported!")
然后我們導入sound
試下
>>> import sound
sound package is getting imported!
>>> sound
<module 'sound' from '/home/bernd/packages/sound/__init__.py'>
>>> sound.effects
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'sound' has no attribute 'effects'
如果你想使用子package的內容复局,但是在父package的__init__.py
的文件內并沒有導入冲簿,你需要手動導入
>>> import sound.effects
effects package is getting imported!
>>> sound.effects
<module 'sound.effects' from '/home/bernd/packages/sound/effects/__init__.py'>
如果你希望python
幫你自動導入sound.effects
你可以往sound
目錄下的__init__.py
文件寫入
"""An empty sound package
This is the sound package, providing hardly anything!"""
import sound.effects
print("sound package is getting imported!")
那么你下次運行的時候python
就會自動幫你導入sound.effects
>>> import sound
effects package is getting imported!
sound package is getting imported!
當然了,除了使用絕對路徑你可以使用相對路徑來導入sound.effects
"""An empty sound package
This is the sound package, providing hardly anything!"""
from . import effects
print("sound package is getting imported!")
這跟linux
的命令行比較像肖揣,.
代表當前目錄民假,..
代表上級目錄
所以你可以在sound.effects
的__init__.py
文件內寫入
from .. import formats
來導入sound.formats
浮入。
當你使用sound
的時候就會發(fā)現龙优,sound.effects
和sound.formats
都被導入了
>>> import sound
formats package is getting imported!
effects package is getting imported!
sound package is getting imported!
最后我想給你展示下,怎么從sound.effects
導入sound.filters.karaoke
模塊事秀,將一下代碼加入到sound.effects
的__init__.py
文件中
"""An empty effects package
This is the effects package, providing hardly anything!"""
from .. import formats
from ..filters import karaoke
print("effects package is getting imported!")
激活python
的交互環(huán)境以后彤断,嘗試import sound
>>> import sound
formats package is getting imported!
filters package is getting imported!
Module karaoke.py has been loaded!
effects package is getting imported!
sound package is getting imported!
現在我們可以使用karaoke
的函數了
>>> sound.filters.karaoke.func1()
Funktion func1 has been called!
>>>
把你的整個package都導入進來
還是用前面的例子,這一次易迹,我會額外的加入一個叫做foobar
的模塊在主目錄宰衙,你可以在這里下載例子
我們嘗試使用*
來進行全部的導入
>>> from sound import *
sound package is getting imported!
我們可以看到僅僅是導入了sound
這個package但是其他的內容并沒有導入。
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
__all__
我們可以使用__all__
這個魔法變量來手動導入模塊和子package睹欲,當你定義了__all__
到__init__.py
文件以后供炼,python
會根據你在list內給出的元素進行逐個導入
__all__ = ["formats", "filters", "effects", "foobar"]
所以我們可以再次導入試試
>>> from sound import *
sound package is getting imported!
formats package is getting imported!
filters package is getting imported!
effects package is getting imported!
The module foobar is getting imported
看下dir()
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'effects', 'filters', 'foobar', 'formats']
>>>
你會發(fā)現所有模塊都已經被順利導入。
那如果我們僅僅導入sound.effects
package內所有內容呢窘疮,會發(fā)生什么袋哼,我們import
的時候到底import
的是什么。
我們看下結果
>>> from sound.effects import *
sound package is getting imported!
effects package is getting imported!
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>>
你會發(fā)現他僅僅是導入了sound.effects
這個package闸衫,跟你沒有修改sound
的__init__.py
之前是類似情況涛贯,僅僅是導入了這個package。
所以你也可以修改sound.effects
的__init__.py
文件來導入effects
內的所有模塊
__all__ = ["echo", "surround", "reverse"]
看下結果
>>> from sound.effects import *
sound package is getting imported!
effects package is getting imported!
Module echo.py has been loaded!
Module surround.py has been loaded!
Module reverse.py has been loaded!
>>>
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'echo', 'reverse', 'surround']
>>>
總結
- 在
from package import *
語句中蔚出,如果__init__.py
中定義了__all__
魔法變量弟翘,那么在__all__
內的所有元素都會被作為模塊自動被導入(ImportError
任然會出現虫腋,如果自動導入的模塊不存在的話)。 - 如果
__init__.py
中沒有__all__
變量稀余,導出將按照以下規(guī)則執(zhí)行:- 此 package 被導入悦冀,并且執(zhí)行
__init__.py
中可被執(zhí)行的代碼 -
__init__.py
中定義的 variable 被導入 -
__init__.py
中被顯式導入的 module 被導入
- 此 package 被導入悦冀,并且執(zhí)行