Python子產品(Python Module)
一般來說,在程式設計語言中。包、庫、子產品都是指同一個概念,是一種代碼的組織方式,将一種功能或若幹種封裝起來,供自己或他人使用。
Python中隻有一種組織方式,稱為子產品。
module
- Python中的子產品module,指的是Python源代碼檔案
- python中的一個類。子產品導入後,會執行個體化一個module對象友善使用
package
- 包package是一種特殊的module
- 為了便利子產品的組織,使用檔案夾(目錄)加
檔案來作為包,包也是一個子產品,檔案夾中__init__.py
的内容則視為是包的内容。在包中的其他源代碼檔案則為包的子子產品。__init__.py
import
在python中,可以使用import語句來導入子產品,導入後會生成一個子產品對象綁定到本地的名詞空間中,對象擁有子產品中所有的對象。可以通過點運算符來使用。
python中隻要一個帶有
__init__.py
的檔案夾或者一個
.py
檔案都視為是一個子產品。都可以通過import語句導入。import有以下幾種用法。
測試子產品的檔案結構
m
+--m1
+--__init__.py
+--m2
+--__init__.py
+--__init__.py
+--test.py
#不帶字尾的為包,并且都帶有各自的__init__.py檔案
m - __init__.py
x = 100 在m子產品中有一個屬性x=100
導入方式
- import module
print(dir())
import m
print(dir())
print(type(m))
print(m.x)
#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <class 'module'>
#100
可以看到導入後本地名詞空間中增加了一個新對象,與子產品名相同。并且可以使用點運算符通路到子產品中的成員。
- import module as name
print(dir())
import m as alias
print(dir())
print(type(alias))
print(alias.x)
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'alias']
# <class 'module'>
#100
增加到名詞空間中的對象名稱為as後的名稱。使用效果與直接導入相同,原名稱m并不在其中。
- import package.module
import m.test
print(dir())
print(m.test)
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
# <module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>
注意:import隻能導入子產品,即無法通過import module.attribute來導入子產品中的屬性。
這裡可以發現即使使用了
import m.test
,綁定的名詞依舊是m。那麼這樣與直接導入m有什麼差異呢?
import m
print(dir())
print(m.test)
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'm']
#!AttributeError: module 'm' has no attribute 'test'
發現隻導入頂級子產品的話,直接使用頂級子產品下的其他子產品就會引發
AttributeError
屬性異常。不存在test屬性,即test子產品沒有被導入。
import m.test
print(m.test)
print(m.m1)
#<module 'm.test' from 'D:\\PythonRoot\\module_test\\m\\test.py'>
#!AttributeError: module 'm' has no attribute 'm1'
使用
import m.test
發現,隻有test子產品被加載了,與test同級的m1沒有被加載
import m.m1.m2
print(m)
print(m.m1)
print(m.m1.m2)
# <module 'm' from 'D:\\PythonRoot\\module_test\\m\\__init__.py'>
# <module 'm.m1' from 'D:\\PythonRoot\\module_test\\m\\m1\\__init__.py'>
# <module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>
導入子子產品時,父子產品也會被導入
- import package.package.module as name
import m.m1.m2 as alias
print(alias)
#<module 'm.m1.m2' from 'D:\\PythonRoot\\module_test\\m\\m1\\m2\\__init__.py'>
可以通過as name的方式,直接将子產品綁定到名詞上,友善使用。
總結
- 通過import導入子產品後會生成一個子產品對象,可以通過子產品對象通路到其中的對象。
- import隻能導入子產品
- 加載子子產品,則一定會加載其父子產品
- import分兩步,加載和将名詞導入。在加載時,隻會加載顯式導入的子產品,例如
隻會加載m子產品和test子產品,雖然名詞空間中隻有m,但是可以使用import m.test
通路到test子產品。m.test
隻會加載m子產品,使用import m
時則會導緻異常。m.test
- 在名詞導入時,預設隻會将頂級子產品名導入,使用as則會把子產品綁定到指定的名詞并導入。
from import
在Python中,為了友善使用還提供了另一種導入方式,即
from import
用法
- from package import module
from m import test
print(dir())
#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test']
通過這種方式能直接把test子產品對象的名稱直接導入到目前的名詞空間中,相當于
import m.test as test
,這裡的package可以是非頂級的包。即可以用
import m.m1 import m2
的方式來導入m2子產品。
- from module import attribute
from m import x
print(dir())
print(x)
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'x']
# 100
可以使用這種方式直接導入子產品中的屬性
- from import 能夠導入子產品或者屬性,那麼如果在一個子產品下有一個子子產品和其屬性同名呢?
在m的
__init__.py
檔案中加入
test = 'attribute m.test'
from m import test
print(test)
#attribute m.test
可以發現導入對象并不是一個子產品,即from import會優先導入非子產品的屬性
總結
- from import能夠導入子產品或者屬性,并且會将導入的子產品或者屬性直接加入到本地名詞空間中
- from後必須是一個子產品,語句無法從其他對象導入屬性
- 如果有同名的屬性和子產品,會優先導入非子產品屬性
子產品對象屬性
在Python3之中,萬物皆對象,module也是一個對象,可以通過其
__dict__
來檢視其屬性。
import m
print(m.__dict__.keys())
#dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'test'])
發現除了x和test自己添加的屬性外,預設還帶有許多屬性。
變量名 | 類型 | 作用 |
---|---|---|
| str | 子產品名 |
| str | 文檔字元串 |
| str | 所在的包名,如果是包,則是自身名稱 |
| | 子產品使用的标準 |
| list | 包的檔案夾所在的路徑。隻有包有該屬性。 |
| str | 檔案的路徑。如果是包,則是 檔案的 路徑。 |
| str | 記錄子產品編譯後生成 檔案的位置 |
查找順序和加載方式
Python中使用一個list來存儲搜尋子產品時的路徑,優先級從低到高依次降低。從索引0開始查找,隻要找到了就停止,找不到傳回importError異常。
sys.path
sys.path
sys.path
中存放了查找子產品時所用到的路徑。是一個有序的可寫的list,可以使用list操作往其中增加或删減路徑。
注意:
sys.path
必須存放絕對路徑
sys.module
sys.module
在m子產品中加入列印語句
print(__name__)
import m
import m
#m
可以發現列印語句隻執行了一次,說明在Python中存在一個導入機制,不會導入重複的子產品。
Python将導入的子產品放到了
sys.module
,是一個dict類型。解釋器在運作時,發現要加載的子產品已經加載過了,就隻使用一個名稱來綁定這個子產品,不再重新導入。我們可以也可以使用這個字典直接來通路已經加載過的子產品。
主子產品
- 程式中的第一個入口,其他子產品皆為該子產品直接或間接加載
- 運作的第一個
檔案.py
-
屬性名為__name__
'__main__'
if __name__ == '__main__':
pass
利用這一特性,可以用上面文法來寫子產品的測試語句。隻有當此子產品為主子產品時才會執行其中語句。
絕對導入和相對導入
絕對導入
文章至此,使用的都是絕對導入,使用一個子產品名,從
sys.path
中搜尋路徑導入的方式都是絕對導入。
相對導入
- 相對導入隻能在
中使用from import
- 隻能在一個包内使用
- 使用相對導入後的子產品無法作為主子產品來使用
用法
與絕對路徑相似,但在from後使用相對路徑
符号 | 含義 |
---|---|
| 目前目錄 |
| 上級目錄 |
| 上上級目錄 |
比如可以在m2中使用
from ... import test
來通路m下的test子產品,也可以使用
from ..package import module
來導入同級目錄包内的子產品,
from .module import attribute
導入同級目錄下子產品屬性。
需要注意的是,如果是包,是以
__init__.py
檔案位置為準,并非檔案夾。
from import *
from import *
-
可以使用from import
的方式來簡易得導入多個屬性from module import *
- 預設隻會導入非下劃線開頭的變量名,不會導入子產品,如果導入的子產品中顯式地導入了其他子產品,且導入子產品使用的變量名非下劃線開頭,該子產品對象也會被導入。
-
屬性__all__
- 子產品預設沒有定義
屬性,當沒有該屬性時,__all__
隻會導入非下劃線開頭以及非子產品屬性。from import *
-
是一個可疊代對象,常用tuple和list__all__
- 如果定義了
,__all__
隻會導入其中的變量,并且不管是否是子產品(這裡的子產品指的是未被加載的子子產品)或是下劃線開頭。from import *
- 子產品預設沒有定義