目錄
一、簡單模塊化
可以把函數、類、常量拆分到不同的文件,把它們放在同一個文件夾,然後使用 from your_file import function_name,class_name 的方式調用,之後,這些函數可以在文件內直接使用。
# utils.py
def get_sum(a, b):
return a + b
# class_utils.py
class Encoder(object):
def encode(self, s):
return s[::-1]
class Decoder(object):
def decode(self, s):
return ''.join(reversed(list(s)))
# class_utils.py
class Encoder(object):
def encode(self, s):
return s[::-1]
class Decoder(object):
def decode(self, s):
return ''.join(reversed(list(s)))
以上代碼,get_sum()函數定義在utils.py,Encoder和Decoder類則在class_utils.py可以直接在main函數直接調用from import,就可以將我們需要的東西import過來。
文件結構如下:
.
├── utils
│ ├── utils.py
│ └── class_utils.py
├── src
│ └── sub_main.py
└── main.py
很容易可以看出,main.py調用目錄的模塊時,只需要使用.代替/來表示子目錄,utils.utils表示utils子文件理的utils.py模塊。
除了一些特殊的情況,import必須位於最前端。
我們還需要在模塊所在的文件夾新建一個__init__.py,內容可以爲空,也可以用來表述包對外暴露的模塊接口,不過,事實上,這是python2的規範。python3中,__init__.py並不是必須的。
二、項目模塊化
在Linux中,以/開頭,來表示從根目錄到葉子節點的路徑,eg:/home/ubuntn/Desktop/my_project/test.py這種方法叫做絕對路徑。
另外,對於任意二個文件,我們都有一條通路可以從一個文件到另一個文件。eg:/home/ubuntu/Downloads/example.json。如果從test.py訪問,需要寫成../../Downloads/example.json,表示上一層目錄,這種方法表示相對路徑。
通常一個python文件在運行時,都會有一個位置,最開始爲這個文件所在的文件夾,當然,這個路徑可以被改變。運行sys.path.append("..")則可以改變當前python解釋器的位置。相對路徑不是一種好的選擇,因爲如果代碼遷移,相對位置會使得重構,也會出錯。對於一個獨立的項目,所有的模塊的追尋方式,最好從項目的根目錄開始,這叫絕對路徑。
絕對路徑的優點:
(1)簡化依賴管理
(2)版本統一,不存在使用一個新模塊,卻導致一系列函數崩潰的情況,並且所有的升級都需要通過單元測試纔可以繼續。
(3)代碼追溯,可以容易的追溯,一個API是從哪裏被調用的,它的歷史版本是怎麼迭代開發的,產生變化的。
做項目時,不可能把全世界的代碼都放在一個文件下,但是類似模塊化的思想還是要有的,那就是以項目作爲最基本的目錄,所有的模塊調用,都要通過根目錄一層層向下索引的方式來import.
可以使用pycharm來創建一個項目,項目結構爲:
.
├── proto
│ ├── mat.py
├── utils
│ └── mat_mul.py
└── src
└── main.py
# utils/mat_mul.py
from proto.mat import Matrix
def mat_mul(matrix_1: Matrix, matrix_2: Matrix):
assert matrix_1.m == matrix_2.n
n, m, s = matrix_1.n, matrix_1.m, matrix_2.m
result = [[0 for _ in range(n)] for _ in range(s)]
for i in range(n):
for j in range(s):
for k in range(m):
result[i][k] += matrix_1.data[i][j] * matrix_2.data[j][k]
return Matrix(result)
# src/main.py
from proto.mat import Matrix
from utils.mat_mul import mat_mul
a = Matrix([[1, 2], [3, 4]])
b = Matrix([[5, 6], [7, 8]])
print(mat_mul(a, b).data)
########## 輸出 ##########
[[19, 22], [43, 50]]
這個例子和上面的像,但注意utils/mat_mul.py,會發現,它import Matrix的方式是from proto.mat這種做法,直接從項目目錄中導入,並依次向下導入模塊mat.py中和Matrix,而不是使用..導入上一級文件夾。
接下來和項目都是使用pycharm來構建。把不同模塊放在不同文件裏,跨模塊調用是從頂層直接索引,一步到位,非常方便。
實際上,python解釋器遇到到import時,它會在一個特定的列表中尋找模塊,,這個特定的列表,可以在下面方式得到:
import sys
print(sys.path)
########## 輸出 ##########
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']
第一項爲空,是因爲將第一項高爲根目錄的絕對地址。這樣,每次運行main.py,import函數在執行時,都會去項目根目錄中找相應的包。
使一般的python環境楞也能做到,兩種方法
import sys
sys.path[0] = '/home/ubuntu/workspace/your_projects'
三、神奇的if__name__=='__main__'
python也可以直接寫代碼,if __name__ == '__main__'。
項目結構如下:
.
├── utils.py
├── utils_with_main.py
├── main.py
└── main_2.py
# utils.py
def get_sum(a, b):
return a + b
print('testing')
print('{} + {} = {}'.format(1, 2, get_sum(1, 2)))
# utils_with_main.py
def get_sum(a, b):
return a + b
if __name__ == '__main__':
print('testing')
print('{} + {} = {}'.format(1, 2, get_sum(1, 2)))
# main.py
from utils import get_sum
print('get_sum: ', get_sum(1, 2))
########## 輸出 ##########
testing
1 + 2 = 3
get_sum: 3
# main_2.py
from utils_with_main import get_sum
print('get_sum: ', get_sum(1, 2))
########## 輸出 ##########
get_sum_2: 3
import 在導入文件時,會自動把所有暴露在外面的代碼全都執行一遍,如果你要把一個東西封裝成模塊,又想讓它可以執行,必做將執行代碼放在if __name__=='__main__'下面。
其實,__name__作爲python的魔術內置參數,本質上是模塊對象的珍上屬性,使用import語句時,__name__就會賦值爲該模塊的名字,自然不等於__main__了。
四、總結
1.通過絕對路徑和相對路徑,我們可以import模塊。
2.在大型工程中模塊化非常重要,模塊的索引通過絕路徑來做,而絕對路徑從程序的根目錄開始
3.記着巧用if __name__ == ‘__main__’來避開import時執行。
五、思考題
from module_name import *和import module_name有什麼區別?
from module_name import *是導入module_name內所有內容,可以直接調用內部方法;import module_name,則是導入module_name在代碼中必須寫成module_name.function的形式。