當我開始學習Python的時候,有些事我希望我一早就知道。我花費了很多時間才學會這些東西。我想要把這些重點都編纂到一篇文章當中。這篇文章的目标讀者,是剛剛開始學習Python語言的有經驗的程式員,想要跳過前幾個月研究Python使用的那些他們已經在用的類似工具。包管理和标準工具這兩節對于初學者來說同樣很有幫助。
我的經驗主要基于Python 2.7,但是大多數的工具對任何版本都有效。
1
2
3
<code>pelican==3.3</code>
<code>Markdown</code>
<code>pelican-extended-sitemap==1.0.0</code>
Python 程式包有一個缺陷是,它們預設會進行全局安裝。我們将要使用一個工具,使我們每個項目都有一個獨立的環境,這個工具叫virtualenv。我們同樣要安裝一個更進階的包管理工具,叫做pip,他可以和virtualenv配合工作。
首先,我們需要安裝pip。大多數python安裝程式已經内置了<code>easy_install</code>(python預設的包管理工具),是以我們就使用<code>easy_install pip</code>來安裝pip。這應該是你最後一次使用<code>easy_install</code>了。如果你并沒有安裝<code>easy_install</code> ,在linux系統中,貌似從<code>python-setuptools</code> 包中可以獲得。
如果你使用的Python版本高于等于3.3, 那麼Virtualenv 已經是标準庫的一部分了,是以沒有必要再去安裝它了。
<code>sudo</code> <code>pip</code><code>install</code> <code>virtualenvwrapper</code>
當virtualenvwrapper安裝後,它會把virtualenv列為依賴包,是以會自動安裝。
打開一個新的shell,輸入<code>mkvirtualenv test</code> 。如果你打開另外一個shell,則你就不在這個virtualenv中了,你可以通過<code>workon test</code> 來啟動。如果你的工作完成了,可以使用<code>deactivate</code> 來停用。

IPython是标準Python互動式的程式設計環境的一個替代品,支援自動補全,文檔快速通路,以及标準互動式程式設計環境本應該具備的很多其他功能。
當你處在一個虛拟環境中的時候,可以很簡單的使用<code>pip install ipython</code> 來進行安裝,在指令行中使用<code>ipython</code> 來啟動
另一個不錯的功能是”筆記本”,這個功能需要額外的元件。安裝完成後,你可以使用<code>ipython notebook</code>,而且會有一個不錯的網頁UI,你可以建立筆記本。這在科學計算領域很流行。
我推薦使用<code>nose</code>或是<code>py.test</code>。我大部分情況下用<code>nose</code>。它們基本上是類似的。我将講解nose的一些細節。
這裡有一個人為建立的可笑的使用nose進行測試的例子。在一個以<code>test_</code>開頭的檔案中的所有以<code>test_</code>開頭的函數,都會被調用:
<code>def</code> <code>test_equality():</code>
<code> </code><code>assert</code> <code>True</code> <code>=</code><code>=</code> <code>False</code>
不出所料,當運作nose的時候,我們的測試沒有通過。
4
5
6
7
8
9
10
11
12
13
<code>(</code><code>test</code><code>)jhaddad@jons-mac-pro ~VIRTUAL_ENV</code><code>/src</code><code>$ nosetests </code>
<code>F</code>
<code>======================================================================</code>
<code>FAIL: test_nose_example.test_equality</code>
<code>----------------------------------------------------------------------</code>
<code>Traceback (most recent call last):</code>
<code> </code><code>File</code><code>"/Users/jhaddad/.virtualenvs/test/lib/python2.7/site-packages/nose/case.py"</code><code>, line 197,</code><code>in</code> <code>runTest</code>
<code> </code><code>self.</code><code>test</code><code>(*self.arg)</code>
<code> </code><code>File</code><code>"/Users/jhaddad/.virtualenvs/test/src/test_nose_example.py"</code><code>, line 3,</code><code>in</code> <code>test_equality</code>
<code> </code><code>assert True == False</code>
<code>AssertionError</code>
nose.tools中同樣也有一些便捷的方法可以調用
<code>from</code> <code>nose.tools</code><code>import</code> <code>assert_true</code>
<code> </code><code>assert_true(</code><code>False</code><code>)</code>
如果你想使用更加類似JUnit的方法,也是可以的:
<code>from</code> <code>unittest</code><code>import</code> <code>TestCase</code>
<code>class</code> <code>ExampleTest(TestCase):</code>
<code> </code><code>def</code> <code>setUp(</code><code>self</code><code>):</code><code># setUp & tearDown are both available</code>
<code> </code><code>self</code><code>.blah</code><code>=</code> <code>False</code>
<code> </code><code>def</code> <code>test_blah(</code><code>self</code><code>):</code>
<code> </code><code>self</code><code>.assertTrue(</code><code>self</code><code>.blah)</code>
開始測試:
14
<code>FAIL: test_blah (test_nose_example.ExampleTest)</code>
<code> </code><code>File</code><code>"/Users/jhaddad/.virtualenvs/test/src/test_nose_example.py"</code><code>, line 11,</code><code>in</code> <code>test_blah</code>
<code> </code><code>self.assertTrue(self.blah)</code>
<code>AssertionError: False is not</code><code>true</code>
<code>Ran 1</code><code>test</code> <code>in</code> <code>0.003s</code>
<code>FAILED (failures=1)</code>
<code>import</code> <code>mock</code>
<code>from</code> <code>mock</code><code>import</code> <code>patch</code>
<code>from</code> <code>time</code><code>import</code> <code>sleep</code>
<code>class</code> <code>Sweetness(</code><code>object</code><code>):</code>
<code> </code><code>def</code> <code>slow_remote_call(</code><code>self</code><code>):</code>
<code> </code><code>sleep(</code><code>10</code><code>)</code>
<code> </code><code>return</code> <code>"some_data"</code> <code># lets pretend we get this back from our remote api call</code>
<code>def</code> <code>test_long_call():</code>
<code> </code><code>s</code><code>=</code> <code>Sweetness()</code>
<code> </code><code>result</code><code>=</code> <code>s.slow_remote_call()</code>
<code> </code><code>assert</code> <code>result</code><code>=</code><code>=</code> <code>"some_data"</code>
當然,我們的測試需要很長的時間。
<code>(</code><code>test</code><code>)jhaddad@jons-mac-pro ~VIRTUAL_ENV</code><code>/src</code><code>$ nosetests test_mock.py </code>
<code>Ran 1</code><code>test</code> <code>in</code> <code>10.001s</code>
<code>OK</code>
太慢了!是以我們會問自己,我們在測試什麼?我們需要測試遠端調用是否有用,還是我們要測試當我們獲得資料後要做什麼?大多數情況下是後者。讓我們擺脫這個愚蠢的遠端調用吧:
15
<code> </code><code>with patch.</code><code>object</code><code>(s,</code><code>"slow_remote_call"</code><code>, return_value</code><code>=</code><code>"some_data"</code><code>):</code>
<code> </code><code>result</code><code>=</code> <code>s.slow_remote_call()</code>
好吧,讓我們再試一次:
<code>.</code>
<code>Ran 1</code><code>test</code> <code>in</code> <code>0.001s</code>
好多了。記住,這個例子進行了荒唐的簡化。就我個人來講,我僅僅會忽略從遠端系統的調用,而不是我的資料庫調用。
nose-progressive是一個很好的子產品,它可以改善nose的輸出,讓錯誤在發生時就顯示出來,而不是留到最後。如果你的測試需要花費一定的時間,那麼這是件好事。
<code>pip install nose-progressive</code> 并且在你的<code>nosetests</code>中添加<code>--with-progressive</code>
iPDB是一個極好的工具,我已經用它查出了很多匪夷所思的bug。<code>pip install ipdb</code> 安裝該工具,然後在你的代碼中<code>import ipdb; ipdb.set_trace()</code>,然後你會在你的程式運作時,獲得一個很好的互動式提示。它每次執行程式的一行并且檢查變量。
<code>a</code><code>=</code> <code>1</code>
<code>b</code><code>=</code> <code>2</code>
<code>a</code><code>=</code> <code>b</code>
這裡是對這個程式的追蹤結果:
<code>(</code><code>test</code><code>)jhaddad@jons-mac-pro ~VIRTUAL_ENV</code><code>/src</code><code>$ python -m trace --trace tracing.py 1 ↵ </code>
<code> </code><code>--- modulename: tracing, funcname: <module></code>
<code>tracing.py(1): a = 1</code>
<code>tracing.py(2): b = 2</code>
<code>tracing.py(3): a = b</code>
<code> </code><code>--- modulename: trace, funcname: _unsettrace</code>
<code>trace.py(80): sys.settrace(None)</code>
當你想要搞清楚其他程式的内部構造的時候,這個功能非常有用。如果你以前用過strace,它們的工作方式很相像
Gevent 是一個很好的庫,封裝了Greenlets,使得Python具備了異步調用的功能。是的,非常棒。我最愛的功能是Pool,它抽象了異步調用部分,給我們提供了可以簡單使用的途徑,一個異步的map()函數:
16
17
18
19
<code>from</code> <code>gevent</code><code>import</code> <code>monkey</code>
<code>monkey.patch_all()</code>
<code>from</code> <code>time</code><code>import</code> <code>sleep, time</code>
<code>def</code> <code>fetch_url(url):</code>
<code> </code><code>print</code> <code>"Fetching %s"</code> <code>%</code> <code>url</code>
<code> </code><code>sleep(</code><code>10</code><code>)</code>
<code> </code><code>print</code> <code>"Done fetching %s"</code> <code>%</code> <code>url</code>
<code>from</code> <code>gevent.pool</code><code>import</code> <code>Pool</code>
<code>urls</code><code>=</code> <code>[</code><code>"http://test.com"</code><code>,</code><code>"http://bacon.com"</code><code>,</code><code>"http://eggs.com"</code><code>]</code>
<code>p</code><code>=</code> <code>Pool(</code><code>10</code><code>)</code>
<code>start</code><code>=</code> <code>time()</code>
<code>p.</code><code>map</code><code>(fetch_url, urls)</code>
<code>print</code> <code>time()</code><code>-</code> <code>start</code>
非常重要的是,需要注意這段代碼頂部對gevent monkey進行的更新檔,如果沒有它的話,就不能正确的運作。如果我們讓Python連續調用 fetch_url 3次,通常我們期望這個過程花費30秒時間。使用gevent:
<code>(test)jhaddad@jons</code><code>-</code><code>mac</code><code>-</code><code>pro ~VIRTUAL_ENV</code><code>/</code><code>src$ python g.py </code>
<code>Fetching http:</code><code>/</code><code>/</code><code>test.com</code>
<code>Fetching http:</code><code>/</code><code>/</code><code>bacon.com</code>
<code>Fetching http:</code><code>/</code><code>/</code><code>eggs.com</code>
<code>Done fetching http:</code><code>/</code><code>/</code><code>test.com</code>
<code>Done fetching http:</code><code>/</code><code>/</code><code>bacon.com</code>
<code>Done fetching http:</code><code>/</code><code>/</code><code>eggs.com</code>
<code>10.001791954</code>
如果你有很多資料庫調用或是從遠端URLs擷取,這是非常有用的。我并不是很喜歡回調函數,是以這一抽象對我來說效果很好。
好吧,如果你看到這裡了,那麼你很可能已經學到了一些新東西。這些工具,在過去的一年裡對我影響重大。找打它們花費了不少時間,是以希望本文能夠減少其他人想要很好利用這門語言需要付出的努力。
轉載 http://www.cnblogs.com/guijinshu/p/ruanjiankaifa.html
QQ:519841366
本頁版權歸作者和部落格園所有,歡迎轉載,但未經作者同意必須保留此段聲明,
且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利