一、包介紹
隨着模塊數目的增多,把所有模塊不加區分地放到一起也是極不合理的,於是Python爲我們提供了一種把模塊組織到一起的方法,即創建一個包。包就是一個含有__init__.py文件的文件夾,文件夾內可以組織子模塊或子包,例如
pool/ #頂級包
├── __init__.py
├── futures #子包
│ ├── __init__.py
│ ├── process.py
│ └── thread.py
└── versions.py #子模塊
#1. 在python3中,即使包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下一定要有該文件,否則import 包報錯
#2. 創建包的目的不是爲了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包的本質就是一種模塊
接下來我們就以包pool爲例來介紹包的使用,包內各文件內容如下
# process.py
def Process():
print('this is process')
# thread.py
def Thread():
print('this is thread')
# versions.py
def check():
print('check versions’)
# __init__.py文件內容均爲空
二、包使用
1、導入包與_init_.py
包屬於模塊的一種,因而包以及包內的模塊均是用來被導入使用的
,而絕非被直接執行,首次導入包(如import pool)同樣會做三件事:
1.執行包下的__init__.py文件
2.產生一個新的名稱空間用於存放__init__.py執行過程中產生的名字
3.在當前執行文件所在的名稱空間中得到一個名字pool,該名字指向__init__.py的名稱空間,例如pool.xxx和pool.yyy中的xxx和yyy都是來自於pool下的__init__.py,也就是說導入包時並不會導入包下所有的子模塊與子包
import pool
pool.versions.check() #拋出異常AttributeError
pool.versions.check()要求pool下有名字versions,進而pool.versions下有名字check。pool.versions下已經有名字check了,所以問題出在pool下沒有名字versions,這就需要在pool下的__init__.py中導入模塊versions
強調:
- 關於包相關的導入語句也分爲import和from … import …兩種,但是無論哪種,無論在什麼位置,在導入時都必須遵循一個原則:
凡是在導入時帶點的,點的左邊都必須是一個包,否則非法
。可以帶有一連串的點,如import 頂級包.子包.子模塊,但都必須遵循這個原則。 - import
只適用於導入模塊
(import 包.子包.模塊),from … import …既可以導入模塊
(from 包 import 模塊),又可以導入一個模塊文件中的方法
(from 包.子包.模塊 import check) - import導入文件時,產生名稱空間中的名字來源於文件,import 包,產生的名稱空間的名字同樣來源於文件,即包下的
__init__.py,導入包本質就是在導入該文件
2、絕對導入與相對導入
針對包內的模塊之間互相導入,導入的方式有兩種
(1)絕對導入:以頂級包爲起始
pool/ #頂級包
├── __init__.py
├── futures #子包
│ ├── __init__.py
│ ├── process.py
│ └── thread.py
└── versions.py #子模塊
重點:包內部的目錄結構通常是包的開發者爲了方便自己管理和維護代碼而創建的,這種目錄結構對包的使用者往往是無用的
,此時通過操作__init__.py可以“隱藏”包內部的目錄結構,降低使用難度
,比如想要讓使用者直接使用pool.check()
那麼應該:
#pool包下的__init__.py文件:
from pool.versions import check
from pool.futures.process import Process
from pool.futures.thread import Thread
# 注意這裏我原來寫的是from pool import versions,只導入了模塊,這樣是不能實現直接 pool.check(),這樣只能pool.versions.check()調用方法。
使用者
,只需要向sys.path中加入模塊搜索路徑
即可使用:
# run.py
import sys
sys.path.append(r'/Users/shijiandingyiqingchun/PycharmProjects/untitled/') # pool包就在這個目錄下
import pool
pool.check()
pool.process()
pool.thread()
# 至此我們實現了導入pool包可以調用pool包中包括子包中所有的方法或變量名!
有人會問,那子包futures下的__init__.py還有存在的必要嗎?
答:有必要,如果我只想導入futures子包中的方法呢?我就可以用import pool.futures,這個時候我們futures下的__init__.py就派上用場了。
(2)相對導入
.代表當前文件所在的目錄,…代表當前目錄的上一級目錄,…上上級目錄,以此類推!
#process.py文件:
import ..versions # 導入versions模塊
(3)一點建議,兩點強調
建議:__init__.py
中要導入包中所有模塊時,建議使用絕對導入
,其他包內的模塊之間的導入,建議使用相對導入。
強調:
-
相對導入只能在包內部使用
- 無論是import還是from-import,但凡是在
導入時帶點的,點的左邊必須是包
,否則語法錯誤
3、from 包.模塊 import *
在使用包時同樣支持from pool.futures import * ,毫無疑問代表的是futures下__init__.py中所有的名字,通用是用變量__all__來控制代表的意思
#futures下的__init__.py
__all__=['process','thread']