附錄B 更多IPython系統(tǒng)相關內容
B.4 使用IPython進行高效代碼開發(fā)的技巧
對于很多用戶來說,以易于開發(fā)严衬、調試和最終交互使用的方式編寫代碼可能是一種習慣的改變周瞎。代碼重新加載等程序細節(jié)可能需要一些調整以及編碼風格方面的考慮。
因此崭参,實現本節(jié)中所介紹的大多技巧更多的是藝術而不是科學,并且需要你進行一些實驗來確定一種對你有效的Python代碼編寫方法款咖。最終何暮,你希望以易于迭代使用的方式構建代碼,并能夠盡可能輕松地探索程序或函數運行的結果铐殃。我發(fā)現使用IPython設計的軟件比僅用作獨立命令行應用程序的代碼更易于使用海洼。這一點變得尤為重要,特別是出現了問題使你不得不對程序中你自己或別人在數月或數年前寫的代碼進行檢查的時候富腊。
B.4.1 重載模塊依賴項
在Python中坏逢,當你輸入import some_lib時,some_lib中的代碼將被執(zhí)行赘被,并且所有定義的變量是整、函數和導入都將存儲在新創(chuàng)建的some_lib模塊命名空間中。之后你再輸入import some_lib時民假,你將獲得對現有模塊命名空間的引用浮入。在交互式IPython代碼開發(fā)中可能會遇到潛在的困難,比如當你以%run命令運行一個依賴于其他模塊的腳本羊异,而依賴的模塊你可能已經做了修改的時候事秀。假設我在test_script.py中有以下代碼:
import some_lib
x = 5
y = [1, 2, 3, 4]
result = some_lib.get_answer(x, y)
如果要執(zhí)行%run test_script.py,然后修改some_lib.py野舶,則下次執(zhí)行%run test_script.py時易迹,由于Python模塊系統(tǒng)是“一次加載”的,你仍然會得到舊版本的some_lib.py平道。這種行為不同于其他數據分析環(huán)境赴蝇,如MATLAB,它會自動傳播代碼的變更巢掺。為了解決這個問題句伶,你有幾個選擇。
- 第一種方法是在標準庫的importlib模塊中使用reload函數:5
import some_lib
import importlib
importlib.reload(some_lib)
上面的代碼保證你每次運行test_script.py時都會得到一個新的some_lib.py副本陆淀。顯然考余,如果依賴關系變得更深,那么在整個地方插入reload的用法可能有點棘手轧苫。對于這個問題楚堤,IPython有一個特殊的dreload函數(不是一個魔術函數),用于模塊的“深層”(遞歸)重載含懊。如果我要運行some_lib.py然后輸入dreload(some_lib)身冬,它將嘗試重新加載some_lib及其所有依賴項。不幸的是岔乔,并不是所有的情況下都有效酥筝,時候不得不重新啟動IPython。
B.4.2 代碼設計技巧
這里并沒有什么簡單的方法雏门,但是有一些高級的準則嘿歌,這些準則是我在自己工作中發(fā)現的。
B.4.2.1 保持相關對象和數據的存在
看到結構有點像下面這個簡單的例子的命令行代碼并不罕見:
from my_functions import g
def f(x, y):
return g(x + y)
def main():
x = 6
y = 7.5
result = x + y
if __name__ == '__main__':
main()
如果我們要在IPython中運行該程序茁影,你覺得什么地方可能會出錯宙帝?完成后,在main函數中定義的結果或對象都不會在如果我要運行some_lib.py然后輸入dreload(some_lib)募闲,它將嘗試重新加載some_lib及其所有依賴項步脓。不幸的是,并不是所有的情況下都有效浩螺,時候不得不重新啟動IPython靴患。
B.4.2 代碼設計技巧
這里并沒有什么簡單的方法,但是有一些高級的準則年扩,這些準則是我在自己工作中發(fā)現的蚁廓。
B.4.2.1 保持相關對象和數據的存在
看到結構有點像下面這個簡單的例子的命令行代碼并不罕見:
from my_functions import g
def f(x, y):
return g(x + y)
def main():
x = 6
y = 7.5
result = x + y
if __name__ == '__main__':
main()
如果我們要在IPython中運行該程序,你覺得什么地方可能會出錯厨幻?完成后相嵌,在main函數中定義的結果或對象都不會在IPython shell中訪問。更好的方法是直接在模塊的全局命名空間中况脆,執(zhí)行main中所有代碼(如果你想要讓模塊也變得可導入的話饭宾,則在if__name__=='main':代碼塊中執(zhí)行)。這樣格了,當你運行代碼時看铆,你就能夠看到main中定義的所有變量。這種方式和在Jupyter notebook中在代碼單元內定義頂層變量的方式是等價的盛末。
B.4.2.2 扁平優(yōu)于嵌套
深度嵌套的代碼讓我聯想到洋蔥一層層的皮弹惦。在測試或調試某個功能時否淤,為了達到感興趣的代碼,必須剝下多少層洋蔥棠隐?”扁平優(yōu)于嵌套“的觀念是Python之禪的一部分石抡,開發(fā)交互式代碼的時候這種觀念依然有用。使函數和類盡可能地解耦并模塊化助泽,可以使得它們更易于測試(如果你正在編寫單元測試)啰扛、調試以及交互式使用。
B.4.2.3 克服對長文件的恐懼
如果你有Java背景(或者其他什么語言)嗡贺,你可能已經知道要保持文件短小隐解。在很多語言中,這聽起來只是個建議诫睬,冗長通常是一種不好的"代碼味道"煞茫,這表明重構和重組可能是必要的。但是岩臣,在使用IPython開發(fā)代碼的同時溜嗜,使用10個小但內部關聯的文件(每個文件不超過100行)比兩三個更長的文件可能會讓你感到更加頭痛。更少的文件意味著更少的模塊重新加載架谎,并且在編輯時也減少了文件之間的跳躍炸宵。我發(fā)現維護更大的模塊,使每個模塊都具有很高的內部凝聚力谷扣,更加有用也更加Pythonic土全。向解決方案進行迭代之后,有時候將較大的文件重構為較小的文件是有意義的会涎。
顯然裹匙,我不支持將這個論點推向極端,這將會把你的所有代碼放在一個單一的怪異