Python 函數的學習筆記

來源:互聯網
上載者:User


1 函數調用

在程式設計中,函數是指用於進行某種計算的一系列語句的有名稱的組合。定義一個函數時,需要指定函數的名稱並寫下一系列程式語句。之後,就可以使用名稱來“調用”這個函數。前面我們已經見過函數調用的例子:

>>> type(32)
<type 'int'>
這個函數的名稱是 type,括弧中的運算式我們稱之為函數的參數。這個函數調用的結果是參數的類型。

我們通常說函數“接收”參數,並“返回”結果。這個結果稱為傳回值(return value)。

2 類型轉換函式
Python提供了一些可將某個值從一種類型轉換為另一種類型的內建函數。int函數可以把任何可以轉換為整型的值轉換為整型;如果轉換失敗,則會報錯:

>>> int('32')
32
>>> int('Hello')
ValueError: invalid literal for int(): Hello
int可以將浮點數轉換為整數,但不會做四捨五入操作,而是直接捨棄小數部分。

>>> int(3.99999)
3
>>> int(-2.3)
-2
float函數將整數和字串轉換為浮點數:

>>> float(32)
32.0
>>> float('3.14159')
3.14159
最後,str函數將參數轉換為字串:

>>> str(32)
'32'
>>> str(3.14159)
'3.14159'
3 數學函數
Python有一個數學計算模組,提供了大多數常用的數學函數。模組是指包含一組相關的函數的檔案。

要想使用一個模組,需要先將它匯入(import)運行環境:

>>> import math
這個語句將會建立一個名為math的模組對象(module object)。如果列印這個對象,可以看到它的一些資訊:

>>> print math
<module 'math' (built-in)>
模組對象包含了這個模組中定義的函數和變數。若要訪問其中的一個函數,需要同時指定模組名稱和函數名稱,用一個句點(.)分隔。這個格式稱為句點標記法(dot notation)。

>>> ratio = signal_power / noise_power
>>> decibels = 10 * math.log10(ratio)

>>> radians = 0.7
>>> height = math.sin(radians)
上面第一個例子使用了log10來計算以分貝為單位的訊號/雜訊比(假設signal_power和noise_power都已經事先定義好了)。math模組也提供了log函數,用來計算底為e的自然對數。

第二個例子計算radians的正弦值。這個變數名已經暗示了,sin以及cos、tan等三角函數接受的參數是以弧度(radians)為單位的。若要將角度轉換為弧度,可以除以360再乘以2π:

>>> degrees = 45
>>> radians = degrees / 360.0 * 2 * math.pi
>>> math.sin(radians)
0.707106781187
運算式math.pi從math模組中獲得變數pi。這個變數的值是π的近似值,大約精確到15位元字。

如果你瞭解三角函數,可以把上面的結果和2的平方根的一半進行比較:

>>> math.sqrt(2) / 2.0
0.707106781187
4 組合
到現在為止,我們已經分別瞭解了程式的基本元素——變數、運算式和語句,但還沒有接觸如何將它們有機地組合起來。

程式設計語言最有用的特性之一就是可以將各種小的構建塊(building block)組合起來。比如,函數的參數可以是任何類型的運算式,包括算術符號:

x = math.sin(degrees / 360.0 * 2 * math.pi)
甚至還包括函數調用:

x = math.exp(math.log(x+1))
基本上,在任何可以使用值的地方,都可以使用任意運算式,只有一個例外:賦值運算式的左邊必須是變數名稱,在左邊放置任何其他的運算式都是語法錯誤(後面我們還會看到這條規則的例外情況)。

>>> minutes = hours * 60        # 正確
>>> hours * 60 = minutes        # 錯誤!
SyntaxError: can't assign to operator
5 添加新函數
至此,我們都只是在使用Python提供的函數,其實我們也可以自己添加新的函數。函數定義指定新函數的名稱,並提供一系列程式語句。當函數被調用時,這些語句會順序執行。

下面是一個例子:

def print_lyrics():
   print "I'm a lumberjack,and I'm okay."
   print "I sleep all night and I work all day."
def是關鍵字,表示接下來是一個函數定義。這個函數的名稱是print_lyrics。函數名稱的書寫規則和變數名稱一樣:字母、數字和某些標點是合法的,但第一個字元不能是數字。關鍵字不能作為函數名,而且我們應盡量避免函數和變數同名。

函數名後的空括弧表示它不接收任何參數。

函數定義的第一行稱為函數頭(header),其他部分稱為函數體(body)。函數頭應該以冒號結束,函數體整體縮排一級。依照慣例,縮排總是使用4個空格,參看3.14節。函數體的代碼語句行數不限。

本例中print語句裡的字串使用雙引號括起來。單引號和雙引號的作用相同。大部分情況下,人們都使用單引號,只在本例中這樣的特殊情況下才使用雙引號。本例中的字串裡本身就存在單引號(單引號也作為省略符號用,如I'm)。

如果在互動模式裡輸入函數定義,則解譯器會輸出省略符號(...)提示你當前的定義還沒有結束:

 >>> def print_lyrics():
 ...   print "I'm a lumberjack, and I'm okay."
 ...   print "I sleep all night and I work all day."
 ...
想要結束這個函數的定義,需要輸入一個空行(在指令檔中則不需要如此)。

定義一個函數後,會建立一個同名的變數。

 >>> print print_lyrics
 <function print_lyrics at 0xb7e99e9c>
 >>> type(print_lyrics)
 <type 'function'>
變數print_lyrics的值是一個函數對象,其類型是'function'。

調用新建立的函數的方式,與調用內建函數是一樣的:

>>> print_lyrics()
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
定義好一個函數之後,就可以在其他函數中調用它。比如,若想重複上面的歌詞,我們可以寫一個repeat_lyrics函數:

def repeat_lyrics():
   print_lyrics()
   print_lyrics()
然後可以調用repeat_lyrics:

>>> repeat_lyrics()
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
當然,這首歌其實並不是這麼唱的。

6 定義和使用
將前面一節零散的代碼整合起來,整個程式就像下面這個樣子:

def print_lyrics():
   print "I'm a lumberjack, and I'm okay."
   print "I sleep all night and I work all day."

def repeat_lyrics():
   print_lyrics()
   print_lyrics()

repeat_lyrics()
這個程式包含兩個函數定義:print_lyrics和repeat_lyrics。(在解譯器執行程式代碼時)函數定義的執行方式和其他語句一樣,不同的是執行後會建立函數對象。函數體裡面的語句並不會立即執行,而是等到函數被調用時才執行。函數定義不會產生任何輸出。

你可能已經猜到,必須先建立一個函數,才能執行它。換言之,函數定義必須在函數的第一次調用之前先執行。

練習1
將程式的最後一行移動到首行,於是函數調用會先於函數定義執行。運行程式並查看會有什麼樣的錯誤資訊。

練習2
將函數調用那一行放回到末尾,並將函數print_lyrics的定義放到函數repeat_lyrics定義之後。這時候運行程式會發生什嗎?

7 執行流程
為了保證函數的定義先於其首次調用執行,你需要知道程式中語句執行的順序,即執行流程。

執行總是從程式的第一行開始。從上到下,按順序,每次執行一條語句。

函數定義並不會改變程式的執行流程,但應注意函數體中的語句並不立即執行,而是等到函數被調用時執行。

函數調用可以看作程式執行流程中的一個迂迴路徑。遇到函數調用時,並不會直接繼續執行下一條語句,而是跳到函數體的第一行,繼續執行完函數體的所有語句,再跳回到原來離開的地方。

這樣看似簡單,但馬上你會發現,函數體中可以調用其他函數。當程式流程運行到一個函數之中時,可能需要執行其他函數中的語句。但當執行那個函數中的語句時,又可能再需要調用執行另一個函數的語句!

幸好Python對於它運行到哪裡有很好的記錄,所以每個函數執行結束後,程式都能跳回到它離開的地方。直到執行到整個程式的結尾,才會結束程式。

前面這段枯燥的描述,寓意何在?當你閱讀代碼時,並不總是應該一行行按照書寫順序閱讀。有時候,按照執行的流程來閱讀代碼,可能理解效果更好。

8 形參和實參①
我們已經看到,有些內建函數需要傳入參數。比如,當調用math.sin時,需要傳入一個數字作為實參。有的函數需要多個實參:math.pow需要兩個,分別是基數(base)和指數(exponent)。

在函數內部,實參會被賦值給形參。下面的例子是一個使用者自訂的函數,接收一個實參:

def print_twice(bruce):
   print bruce
   print bruce
這個函數在調用時會把實參的值賦到形參bruce上,並將其列印兩次。

這個函數對任何可以列印的值都可用。

>>> print_twice('Spam')
Spam
Spam
>>> print_twice(17)
17
17
>>> print_twice(math.pi)
3.14159265359
3.14159265359
內建函數的組合規則,在使用者自訂函數上也同樣可用,所以我們可以對print_twice使用任何錶達式作為實參:

>>> print_twice('Spam '*4)
Spam Spam Spam Spam
Spam Spam Spam Spam
>>> print_twice(math.cos(math.pi))
-1.0
-1.0
作為實參的運算式會在函數調用之前先執行。所以在這個例子中,運算式'Spam'*4和math.cos(math.pi)都只執行一次。

你也可以使用變數作為實參:

>>> michael = 'Eric, the half a bee.'
>>> print_twice(michael)
Eric, the half a bee.
Eric, the half a bee.
作為實參傳入到函數的變數的名稱(michael)和函數定義裡形參的名稱(bruce)沒有關係。函數內部只關心形參的值,而不用關心它在調用前叫什麼名字;在print_twice函數內部,大家都叫bruce。

9 變數和形參是局部的
當你在函數體內建立一個變數時,它是局部的(local),即它只存在於這個函數之內。比如:

def cat_twice(part1, part2):
  cat = part1 + part2
  print_twice(cat)
這個函數接收兩個實參,將它們拼接起來,並將結果列印兩遍。下面是一個使用這一函數的例子:

>>> line1 = 'Bing tiddle '
>>> line2 = 'tiddle bang.'
>>> cat_twice(line1, line2)
Bing tiddle tiddle bang.
Bing tiddle tiddle bang.
當cat_twice結束時,變數cat會被銷毀。這時再嘗試列印它的話,會得到一個異常:

>>> print cat
NameError: name 'cat' is not defined
形參也是局部的。比如,在print_twicebruce這個變數。

10 棧圖
要跟蹤哪些變數在哪些地方使用,有時候畫一個棧圖(stack diagram)會很方便。和狀態圖一樣,棧圖可以展示每個變數的值,不同的是它會展示每個變數所屬的函數。

每個函數使用一個幀包含,幀在棧圖中就是一個帶著函數名稱的盒子,裡面有函數的參數和變數。前面的函數樣本的棧圖如圖1所示。

 

圖1 棧圖

圖中各個幀從上到下安排成一個棧,能夠展示出哪個函數被哪個函數調用了。在這個例子裡,printtwice被cattwice調用,而cattwice被main調用。_main是用於表示整個棧圖的圖框的特別名稱。當你在所有函數之外建立變數時,它就是屬於__main的。

每個形參都指向與其對應的實參相同的值,所以,part1和line1的值相同,part2和line2的值相同,而bruce和cat的值相同。

如果調用函數的過程中發生了錯誤,Python會列印出函數名,以及調用它的函數的名稱,以及調用這個調用者的函數名,依此類推,一直到main。

比如,如果你在print_twice中訪問cat變數,則會得到一個NameError:

Traceback (innermost last):
  File "test.py", line 13, in __main__
    cat_twice(line1, line2)
  File "test.py", line 5, in cat_twice
    print_twice(cat)
  File "test.py", line 9, in print_twice
    print cat
NameError: name 'cat' is not defined
上面這個函數列表被稱為回溯(traceback)。它告訴你錯誤出現在哪個程式檔案,哪一行,以及哪些函數正在運行。它也會顯示導致錯誤的那一行代碼。

回溯中函數的順序和棧圖中圖框的順序一致。當前正在執行的函數列在最底部。

11 有傳回值函數和無傳回值函數
我們使用過的函數中,有一部分函數,如數學函數,會產生結果。因為沒有想到更好的名字,我稱這類函數為有傳回值函數(fruitful function)。另一些函數,如print_twice,會執行一個動作,但不返回任何值。我們稱這類函數為無傳回值函數(void function)。

當你調用一個有傳回值的函數時,大部分情況下都想要對結果做某種操作。比如,你可能會想把它賦值給一個變數,或者用在一個運算式中:

x = math.cos(radians)
golden = (math.sqrt(5) + 1) / 2
在互動模式中調用函數時,Python會直接顯示結果:

>>> math.sqrt(5)
2.2360679774997898
但是在指令碼中,如果只是直接調用這類函數,那麼它的傳回值就會永遠丟失掉!

math.sqrt(5)
這個指令碼計算5的平方根,但由於並沒有把計算結果儲存到某個變數中,或顯示出來,所以其實沒什麼實際作用。

無傳回值函數可能在螢幕上顯示某些東西,或者有其他的效果,但是它們沒有傳回值。如果你試著把它們的結果賦值給某個變數,則會得到一個特殊的值None。

>>> result = print_twice('Bing')
Bing
Bing
>>> print result
None
值None和字串'None'並不一樣。它是一個特殊的值,有自己獨特的類型:

>>> print type(None)
<type 'NoneType'>
到目前為止,我們自訂的函數都是無傳回值函數。

12 為什麼要有函數
為什麼要花功夫將程式拆分成函數呢?也許剛開始編程的時候這其中的原因並不明晰。下面這些解釋都可作為參考。

建立一個函數,可以讓你有機會給一組語句命名,這樣可以讓代碼更易讀和更易調試。
函數可以通過減少重複代碼使程式更短小。後面如果你需要修改代碼,也只要修改一個地方即可。
將一長段程式拆分成幾個函數後,可以對每一個函數單獨進行調試,再將它們組裝起來成為完整的產品。
一個設計良好的函數,可以在很多程式中使用。書寫一次,調試一次,複用無窮。
13 使用from匯入模組
Python提供了兩種匯入模組的方式;我們已經見過其中一種:

>>> import math
>>> print math
<module 'math' (built-in)>
>>> print math.pi
3.14159265359
如果你匯入math,則會得到名為math的模組對象。模組對象包含了pi這樣的常量以及諸如sin和exp這樣的函數。

但是如果直接存取pi,則會發生錯誤。

>>> print pi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'pi' is not defined
這時候,你可以像下面這樣來匯入模組中的某個對象:

>>> from math import pi
現在就可以直接存取pi,而不需要使用句點標記法math.pi了。

>>> print pi
3.14159265359
或者,也可以使用星號來匯入一個模組的所有成員:

>>> from math import *
>>> cos(pi)
-1.0
用這種方式匯入模組內所有的成員,好處是可以使你的代碼更簡潔,但缺點是不同模組的同名成員之間,或者和自訂的變數之間,可能發生名字衝突。

14 調試
如果你使用文字編輯器來編寫指令碼,則可能會遇到縮排時空格和定位字元混淆的問題。避免這種問題的最好辦法是只使用空格(不用定位字元)。大部分識別Python的文字編輯器都預設這麼處理,不過也有一些不支援。

定位字元和空格都是不可見的,因而會很難調試,所以應嘗試找一個能幫你處理縮排的編輯器。

另外,不要忘了在運行程式前儲存它。有的開發環境會自動儲存,但也有不自動儲存的。如果不儲存,則你寫好的代碼和啟動並執行代碼並不一樣。

如果啟動並執行報錯的代碼和你寫的不一樣,調試時會浪費很多時間!

所以一定要確保你眼前所看的代碼和所啟動並執行代碼是一致的。如果不確定,可以在程式開頭寫一句print 'hello'並再運行一次。如果沒有看到hello輸出,則你啟動並執行不是正確的程式!

15 術語表
函數(function):一個有名稱的語句序列,可以進行某種有用的操作。函數可以接收或者不接收參數,可以返回或不返回結果。

函數定義(function definition):一個用來建立新函數的語句,指定函數的名稱、參數以及它執行的語句序列。

函數對象(function object):函數定義所建立的值。函數名可以用作變數來引用一個函數對象。

函數頭(header):函數定義的第一行。

函數體(body):函數定義內的語句序列。

形參(parameter):函數內使用的用來引用作為實參傳入的值的名稱。

函數調用(function call):執行一個函數的語句。它由函數名稱和參數列表組成。

實參(argument):當函數調用時,提供給它的值。這個值會被賦值給對應的形參。

局部變數(local variable):函數內定義的變數。局部變數只能在函數體內使用。

傳回值(return value):函數的結果。如果函數被當做運算式調用,傳回值就是運算式的值。

有傳回值函數(fruitful function):返回一個值的函數。

無傳回值函數(void function):沒有傳回值的函數。

模組(module):一個包含相關函數以及其他定義的集合的檔案。

import語句(import statement):讀入一個模組檔案,並建立一個模組對象的語句。

模組對象(module object):使用import語句時建立的對象,提供對模組中定義的值的訪問。

句點標記法(dot notation):調用另一個模組中的函數的文法,使用模組名加上一個句點符號,再加上函數名。

組合(composition):使用一個運算式作為更大的運算式的一部分,或者使用語句作為更大的語句的一部分。

執行流程(flow of execution):程式運行中語句執行的順序。

棧圖(stack diagram):函數棧的圖形表達形式,也展示它們的變數,以及這些變數引用的值。

圖框(frame):棧圖中的一個圖框,表達一個函數調用。它包含了局部變數以及函數的參數。

回溯(traceback):當異常發生時,列印出正在執行的函數棧。

16 練習
練習3 Python提供了一個內建函數len,返回一個字串的長度。所以len('allen')的值是5。

編寫一個函數right_justify,接收一個字串形參s,並列印出足夠的前置空白,以達到最後一個字元顯示在第70列上。

>>> right_justify('allen')
                                   allen
練習4 函數對象是一個值,你可以將它賦值給變數,或者作為實參傳遞。例如do_twice是一個函數,接收一個函數對象作為實參,並調用它兩次:

def do_twice(f):
   f()
   f()
下面是一個使用do_twice來調用一個print_spam函數兩次的樣本:

def print_spam():
   print 'spam'

do_twice(print_spam)
1.將這個樣本存入指令碼中並測試它。

2.修改do_twice,讓它接收兩個實參,一個是函數對象,另一個是一個值,它會調用函數對象兩次,並傳入那個值作為實參。

3.編寫一個更通用的print_spam,叫做print_twice,接收一個字串形參,並列印它兩次。

4.使用修改版的do_twice來調用print_twice兩次,並傳入實參'spam'。

5.定義一個新的函數do_four,接收一個函數對象與一個值,使用這個值作為實參調用函數4次。這個函數的函數體應該只有兩個語句,而不是四個。

練習5 這個練習可以只用語句和我們已經學過的其他語言特性實現。

1.編寫一個函數,繪製如下的表格:

 

提示

要在同一行列印多個值,你可以使用逗號分隔不同的值:

print '+','-'
如果值序列的結尾有一個逗號,Python不會換行,所以後面的列印語句會出現在同一行。

print '+',
print '-'
這兩個語句的輸出是'+ -'。 2.編寫一個函數繪製類似的表格,但有4行4列。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.