詳見Python編程中一些重用與縮減的技術

@本文來源於公衆號:csdn2299,喜歡可以關注公衆號 程序員學府

返璞歸真

許多流行的玩具都以這樣一個概念爲基礎:簡單的積木。這些簡單的積木可通過多種方式組合在一起構造出全新的作品 —— 有時甚至完全令人出乎意料。這一概念同樣適用於現實生活中的建築領域,將基本原材料組合在一起,形成有用的建築物。平凡無奇的材料、技術和工具簡化了新建築物的建造過程,同樣也簡化了對新踏入此領域的人員的培訓。

相同的基本概念也適用於計算機程序開發技術,包括以 Python 編程語言編寫的程序。本文介紹了使用 Python 創建基本構件 (building block) 的方法,可用於解決更爲複雜的問題。這些基本構件可能小而簡單,也可能龐大而複雜。無論採用哪種形式,我們這場遊戲的目的就是定義基本構件,然後使用它們來創建專屬於您的傑作。

函數:封裝邏輯

在本系列的前幾篇文章中,您通常不得不重複輸入所有代碼,即便它與上一行代碼完全相同。此要求的惟一特例就是變量的使用:一旦初始化了變量的內容,之後就可以隨時重用。顯而易見,這一用法的普及對我們大有好處。

描述傑出程序員的最流行的箴言之一就是他們很懶惰。這並不表示傑出的程序員不努力工作 —— 而是說他們喜歡靈活的工作方法,除非絕對必要,否則從不反覆做任何相同的事情。這也就意味着在您需要編寫代碼之前,首先考慮如何實現重用。Python 中有多種可實現重用的途徑,但最簡單的技術莫過於使用函數,也稱爲方法 或子例程。

與絕大多數現代編程語言類似,Python 支持使用方法將一組語句封裝在一起,從而可在必要時重複使用。清單 1 給出了一段簡單的僞代碼,爲您展示如何在 Python 中編寫方法。
清單 1. 定義函數的僞代碼

def myFunction(optional input data):
  initialize any local data
  actual statements that do the work
  optionally return any results

如您所見,在 Python 中,函數的基本組成部分是包裝器代碼,指明將被重用的一系列 Python 語句。函數可接受輸入參數,輸入參數在緊接着函數名(在本例中爲 myFunction)之後的圓括號內提供。函數還可返回值(更爲正式的說法是:對象),包括像 tuple 這樣的 Python 容器。

在真正着手構建函數之前,讓我們先來看看關於僞代碼的一些簡單卻重要的要點:

請注意函數名中所用的字符大小寫:大多數字符都是小寫的,但在使用多個單詞連接成一個函數名時,後接的各單詞首字母應大寫(例如,myFunction 中的 F)。這就是所謂的駝峯式大小寫風格 (camel casing),是 Python 和其他編程語言中廣泛採用的一種技術,可使函數的名稱更易閱讀。
函數定義中的程序語句採用了縮進式排版,函數體由 Python 語句塊構成,它們也必須像循環或條件語句那樣縮進。
函數定義的第一行也稱爲方法簽名 (method signature),以 def 開頭(def 是 define 這個單詞的縮寫)。
方法簽名以冒號結尾,表示下面的代碼行是函數體。

至此,您或許已認可了使用方法的好處。那麼讓我們投入進去,開始編寫函數吧。“Discover Python, Part 6: Programming in Python, For the fun of it” 中使用了一個 for 循環來創建乘法表。清單 2 展示了同樣的概念,但本例中創建的是一個函數,用於封裝乘法表計算背後的邏輯。
清單 2. 第一個函數

>>> def timesTable():
...   for row in range(1, 6):
...     for col in range(1, 6):
...       print "%3d " % (row * col),
...     print
... 
>>> timesTable()
 1  2  3  4  5
 2  4  6  8  10
 3  6  9  12  15
 4  8  12  16  20
 5  10  15  20  25
>>> t = timesTable
>>> type(t)
<type 'function'>
>>> t
<function timesTable at 0x64c30>
>>> t()
 1  2  3  4  5
 2  4  6  8  10
 3  6  9  12  15
 4  8  12  16  20
 5  10  15  20  25

timesTable 函數定義起來非常簡單,它不接受任何輸入參數,也不返回任何結果。函數體幾乎與 “Discover Python, Part 6” 中的語句完全相同(但該文章中的乘法表爲從 1 到 10)。爲了調用 方法,並使其發揮作用,只需輸入函數名後接圓括號即可。本例中還輸出了乘法表。

在 Python 中,函數是一類對象,與整型變量和容器對象相同。因而,您可以將函數指派給一個變量(切記,在 Python 中變量是動態類型化的)。在清單 2 中,我們將 timesTable 函數指派給變量 t。接下來的兩行代碼表示變量 t 確實指向函數。最後,我們使用變量 t 調用 timesTable 函數。

## 函數:動態更改邏輯

清單 2 中的 timesTable 函數不復雜,但也不是特別有用。更有用的示例允許您指定用於生成乘法表的行數和列數 —— 換言之,允許您在調用函數時動態地更改函數的操作方式。在函數定義中使用兩個輸入參數即可實現這一功能,如清單 3 所示。
清單 3. 更好的乘法表函數>>> def timesTable2(nrows=5, ncols=5):
...   for row in range(1, nrows + 1):
...     for cols in range(1, ncols + 1):
...       print "%3d " % (row * cols),
...     print
... 
>>> timesTable2(4, 6)
 1  2  3  4  5  6
 2  4  6  8  10  12
 3  6  9  12  15  18
 4  8  12  16  20  24
>>> timesTable2()  
 1  2  3  4  5
 2  4  6  8  10
 3  6  9  12  15
 4  8  12  16  20
 5  10  15  20  25
>>> timesTable2(ncols=3)
 1  2  3
 2  4  6
 3  6  9
 4  8  12
 5  10  15

兩個乘法表函數的定義非常相近,但清單 3 中的函數有用得多(通過清單 3 中的 3 次調用即可看出這一點)。爲函數添加此附加功能的方法非常簡單:提供名爲 nrows 和 ncols 的兩個輸入參數,允許在調用函數時更改乘法表的大小。這兩個參數隨後會被提供給生成乘法表的兩個 for 循環。

關於 timesTable2 函數的另一要點就是兩個輸入參數有默認值。在函數簽名中爲參數提供默認值,方法是在參數名後添加等號和值,例如 nrows=5。默認參數使程序獲得了更高的靈活性,因爲在您調用函數時,可以包含兩個輸入參數,也可以僅包含一個輸入參數,甚至可以一個參數都不包含。但這種方法可能會導致某些問題。如果您在函數調用期間未指定全部參數,則必須顯式地寫出您所指定的參數的名稱,以使 Python 解釋器能夠正確地調用函數。最後一個函數調用正體現了這一點,它顯式地調用了帶有 ncols=3 的 timesTable2 函數,函數創建了一個 5 行(默認值)3 列(所提供的值)的乘法表。

函數:返回數據

使用方法時,人們最希望獲得的結果並非總是乘法表。您可能希望完成一次計算,並將計算結果值返回給調用代碼。有時要實現這兩個目的,需要分別調用不返回任何數據的調用方法(子例程)和返回值的方法(函數)。但在 Python 中,您無需擔心這些語義問題,因爲通過使用 return 語句,幾乎可以相同的方式實現這兩個目的(參見清單 4)。
清單 4. 在函數中返回一個值

>>> def stats(data):
...   sum = 0.0
...   for value in data:
...     sum += value
...   return (sum/len(data))
... 
>>> stats([1, 2, 3, 4, 5])   # Find the mean value from a list
3.0
>>> stats((1, 2, 3, 4, 5))   # Find the mean value from a tuple
3.0
>>> stats()
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
TypeError: stats() takes exactly 1 argument (0 given)
>>> stats("12345")
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
 File "<stdin>", line 4, in stats
TypeError: unsupported operand type(s) for +=: 'float' and 'str'

這個簡單的函數遍歷 data(假設 data 爲一個容納有數字數據的 Python 容器),計算一組數據的平均值,然後返回值。函數定義接受一個輸入參數。平均值通過 return 語句傳回。當您調用帶有包含數字 1 到 5 的 list 或 tuple 的函數時,返回值會顯示在屏幕上。如果調用不帶任何參數的函數、帶非容器數據類型的函數或帶內含非數字數據的容器的函數,就會導致出錯。(在此類情況下拋出錯誤是很有意義的。更高級的處理方法應包含恰當的錯誤檢查和處理,以應對這些情況,但這不在本文討論範圍內。)

此示例已經非常有用,但還可使它更強大,如清單 5 所示。在 Python 中,函數可返回任何有效的對象類型,包括容器類型在內。因此,您可以計算多個數量,並輕鬆地將多個結果返回給調用語句。
清單 5. 返回複合值

>>> def stats(data):
...   sum = 0.0
...   for value in data:
...     sum += value
...   mean = sum/len(data)
...   sum = 0.0
...   for value in data:
...     sum += (value - mean)**2
...   variance = sum/(len(data) - 1)
...   return (mean, variance)
...
>>> stats([1, 2, 3, 4, 5])
(3.0, 2.5)
>>> (m, v) = stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> print m, v
5.0 7.5

爲了從一個函數中返回多個值,要將其括在一個括號中並以逗號分隔 —— 換句話說,創建並返回一個 tuple。新 stats 函數的函數體要略加修改,以計算數字序列的樣本方差。最後,正如 stats 函數的兩次調用所示,tuple 值可作爲一個 tuple 存取,也可將其解包爲各自的分量。

模塊:簡化代碼重用

至此,您或許已相信了代碼重用的價值。但即便是使用函數,您依然需要在打算使用函數時重新輸入函數體。例如,當您打開一個新的 Python 解釋器時,必須鍵入之前所創建的所有函數。幸運的是,您可以使用模塊 將相關函數(和其他 Python 對象)封裝在一起,將其保存在一個文件中,然後將這些已定義好的函數導入到新 Python 代碼內,包含於 Python 解釋器之中。

爲介紹在 Python 中使用模塊的方法,我們將重用清單 5 中的 stats 方法。有兩個選擇:您可以從與本文相關的壓縮文件中提取名爲 test.py 的文件,也可以在編輯器中鍵入函數,然後將文件保存爲 test.py。完成上一步後,在您保存 test.py 的目錄中啓動一個新的 Python 解釋器,然後輸入如清單 6 所示的語句。
清單 6. 使用模塊

>>> import test
>>> test.stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
(5.0, 7.5)
>>> from test import stats
>>> (m, v) = stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> print m, v
5.0 7.5

第一行 import test 打開文件 test.py 並處理文件中的各條語句。這裏僅定義了 stats 函數,但若需要,您還可定義更多的函數。調用 stats 函數時,應以模塊名 test 作爲函數前綴。之所以使用這種複雜的名稱,是出於作用域 方面的考慮,作用域表示一個程序內名稱的有效範圍。爲告知 Python 您要調用的是哪個 stats 方法,就必須提供完整的名稱。這一點非常重要,因爲您可能擁有多個名稱相同的對象。作用域規則可幫助 Python 判斷您想使用的對象。

第三行 from test import stats 也打開了文件 test.py,但它隱式地將 stats 方法置入當前文件的作用域內,以使您能夠直接調用 stats 函數(無需使用模塊名)。明智地使用 from … import … 語法可使您的程序更簡潔,但過度的使用也會導致混淆,甚至出現更糟糕的作用域衝突錯誤。不要濫用您的新武器!

模塊庫

使用 Python 編程語言的一個主要好處就是大型的內置式標準庫,可作爲 Python 模塊訪問。常用模塊示例如下:

math 包含有用的數學函數。
sys 包含用於與 Python 解釋器交互的數據和方法。
array 包含數組數據類型和相關函數。
datetime 包含有用的日期和時間處理函數。

由於這些都是內置模塊,因此您可以通過幫助解釋器來了解更多相關內容,如清單 7 所示。
清單 7. 獲得關於 math 模塊的幫助信息

>>> help(math)
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
NameError: name 'math' is not defined
>>> import math   # Need to import math module in order to use it
>>> help(math)
Help on module math:
NAME
  math
FILE
  /System/Library/Frameworks/Python.framework/Versions/2.4/lib/
python2.4/lib-dynload/math.so
DESCRIPTION
  This module is always available. It provides access to the
  mathematical functions defined by the C standard.
FUNCTIONS
  acos(...)
    acos(x)
     
    Return the arc cosine (measured in radians) of x.
   
  asin(...)
    asin(x)
     
    Return the arc sine (measured in radians) of x.
...

math 模塊的幫助輸出展示了所支持的大量數學函數,包括 sqrt 函數在內。您可以利用此函數將您的樣本方差計算轉換爲樣本標準差計算,如清單 8 所示。
清單 8. 使用多個模塊

>>> from math import sqrt
>>> from test import stats
>>> (m, v) = stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> print m, sqrt(v)
5.0 2.73861278753

如您所見,您可以將多個模塊導入到一個 Python 程序中。在大型、內置的模塊庫與更大量的公用庫(其中許多都是開放源碼的)的共同協助下,您很快也會成爲一名懶惰 —— 也就是傑出 —— 的程序員。

可執行文件

導入一個模塊時,Python 解釋器會處理模塊文件內的各行。實際上,您可以調用 Python 解釋器使其僅處理包含於一個文件中的一個 Python 程序。在基於 UNIX? 的操作系統中,您可以輕鬆創建可執行的文件,如清單 9 所示。
清單 9. 一個完整的 Python 程序

#!/usr/bin/env python
def stats(data):
  sum = 0.0
  for value in data:
    sum += value
  mean = sum/len(data)
  sum = 0
  for value in data:
    sum += (value - mean)**2
  variance = sum/(len(data) - 1)
  return(mean, variance)
(m, v) = stats([1, 2, 3, 4, 5, 6, 7, 8, 9])
print "The mean and variance of the values " \
"from 1 to 9 inclusive are ",m, v

觀察上例,您應該會產生幾分好感,將 Python 程序置於文件內,並使其運行是如此簡單。本例與 test.py 文件中的代碼之間惟一的差異就是包含了第一行。在基於 UNIX 的操作系統中,本行會使 Python 解釋器自動啓動,並在終止前處理文件中的語句。本示例中的其他行定義了 stats 函數、調用了函數,並輸出了結果。

要運行本文件中的語句,您需要啓動一個 Python 解釋器,並讓它去讀取和處理文件的內容。爲實現這一目的,您必須首先將清單 9 中的示例輸入到一個名爲 mystats.py 的文件中,也可從與本文相關的壓縮文件中提取文件。進入包含此文件的目錄,然後按清單 10 中所示命令執行。注意對於 Microsoft? Windows? 操作系統而言,僅應使用第一條命令;其他命令是供 UNIX 系統(如 Linux? 或 Mac OS X)使用的。
清單 10. 執行 Python 程序

rb% python mystats.py
The mean and variance of the values from 1 to 9 inclusive are 5.0 7.5
rb% chmod +x mystats.py
rb% ./mystats.py
The mean and variance of the values from 1 to 9 inclusive are 5.0 7.5

清單 10 中的命令展示了運行一個包含於文件之中的 Python 程序的方法。第一條命令以文件名調用 Python 解釋器,無論使用哪種系統安裝 Python、Python 解釋器位於哪個目錄下,這種方法都有效。第二條命令 chmod 使包含 Python 程序的文件成爲可執行文件。第三條命令告訴操作系統運行程序。這是通過使用 env 程序實現的,這是一種獨立於操作系統的技術,用於定位和運行程序 —— 本例中是 Python 解釋器。

重用與縮減

本文介紹瞭如何在 Python 中編寫可重用代碼。討論瞭如何在 Python 程序中使用方法或可重用塊。方法可接受輸入參數,也可返回數據,包括容器數據類型在內。這種功能使方法成爲一種可處理大量問題的強大途徑。本文還介紹了模塊,模塊可使您將相關方法及數據合併入一個有組織的層次結構中,而此結構可方便地在其他 Python 程序中重用。最後還介紹瞭如何將所有這些內容組合在一起以創建一個功能完整、獨立的 Python 程序。您已經看到,代碼的重用也就意味着您的工作量縮減。對於程序員而言,懶惰是一種優勢而非陋習。
非常感謝你的閱讀
大學的時候選擇了自學python,工作了發現吃了計算機基礎不好的虧,學歷不行這是沒辦法的事,只能後天彌補,於是在編碼之外開啓了自己的逆襲之路,不斷的學習python核心知識,深入的研習計算機基礎知識,整理好了,我放在我們的微信公衆號《程序員學府》,如果你也不甘平庸,那就與我一起在編碼之外,不斷成長吧!

其實這裏不僅有技術,更有那些技術之外的東西,比如,如何做一個精緻的程序員,而不是“屌絲”,程序員本身就是高貴的一種存在啊,難道不是嗎?[點擊加入]想做你自己想成爲高尚人,加油!

非常感謝你的閱讀
大學的時候選擇了自學python,工作了發現吃了計算機基礎不好的虧,學歷不行這是沒辦法的事,只能後天彌補,於是在編碼之外開啓了自己的逆襲之路,不斷的學習python核心知識,深入的研習計算機基礎知識,整理好了,我放在我們的微信公衆號《程序員學府》,如果你也不甘平庸,那就與我一起在編碼之外,不斷成長吧!

其實這裏不僅有技術,更有那些技術之外的東西,比如,如何做一個精緻的程序員,而不是“屌絲”,程序員本身就是高貴的一種存在啊,難道不是嗎?[點擊加入]想做你自己想成爲高尚人,加油!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章