在上一節中,已經明確了函數的基本結構和初步的調用方法。但是,上一節中寫的函數,還有點缺憾,不知道讀者是否覺察到了。我把結果是用`print`語句打印出來的。這是實際編程中廣泛使用的嗎?肯定不是。因為函數在編程中,起到一段具有抽象價值的代碼作用,一般情況下,用它得到一個結果,這個結果要用在其它的運算中。所以,不能僅僅局限在把某個結果打印出來。所以,函數必須返回一個結果。
結論:函數要有返回值,也必須有返回值。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#返回值)返回值
為了能夠說明清楚,先編寫一個函數。還記得斐波那契數列嗎?我打算定義一個能夠得到斐波那契數列的函數,從而實現可以實現任意的數列。你先想想,要怎么寫?
參考代碼:
~~~
#!/usr/bin/env python
# coding=utf-8
def fibs(n):
result = [0,1]
for i in range(n-2):
result.append(result[-2] + result[-1])
return result
if __name__ == "__main__":
lst = fibs(10)
print lst
~~~
把含有這些代碼的文件保存為名為20202.py的文件。在這個文件中,首先定義了一個函數,名字叫做fibs,其參數是輸入一個整數。在后面,通過`lst = fibs(10)`調用這個函數。這里參數給的是10,就意味著要得到n=10的斐波那契數列。
運行后打印數列:
~~~
$ python 20202.py
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
~~~
當然,如果要換n的值,只需要在調用函數的時候,修改一下參數即可。這才體現出函數的優勢呢。
觀察fibs函數,最后有一個語句`return result`,意思是將變量result的值返回。返回給誰呢?一般這類函數調用的時候,要通過類似`lst = fibs(10)`的語句,那么返回的那個值,就被變量lst貼上了,通過lst就能得到該值。如果沒有這個賦值語句,雖然函數照樣返回值,但是它飄忽在內存中,我們無法得到,并且最終還被當做垃圾被python回收了。
注意:上面的函數之返回了一個返回值(是一個列表),有時候需要返回多個,是以元組形式返回。
~~~
>>> def my_fun():
... return 1,2,3
...
>>> a = my_fun()
>>> a
(1, 2, 3)
~~~
有的函數,沒有renturn,一樣執行完畢,就算也干了某些活兒吧。事實上,不是沒有返回值,也有,只不過是None。比如這樣一個函數:
~~~
>>> def my_fun():
... print "I am doing somthin."
...
~~~
我在交互模式下構造一個很簡單的函數,注意,我這是構造了一個簡單函數,如果是復雜的,千萬不要在交互模式下做。如果你非要做,是能嘗到苦頭的。
這個函數的作用就是打印出一段話。也就是執行這個函數,就能打印出那段話,但是沒有return。
~~~
>>> a = my_fun()
I am doing somthin.
~~~
我們再看看那個變量a,到底是什么
~~~
>>> print a
None
~~~
這就是這類只干活兒,沒有`return`的函數,返回給變量的是一個`None`。這種模樣的函數,通常不用上述方式調用,而采用下面的方式,因為他們返回的是None,似乎這個返回值利用價值不高,于是就不用找一個變量來接受返回值了。
~~~
>>> my_fun()
I am doing somthin.
~~~
特別注意那個return,它還有一個作用。觀察下面的函數和執行結果,看看能不能發現它的另外一個作用。
~~~
>>> def my_fun():
... print "I am coding."
... return
... print "I finished."
...
>>> my_fun()
I am coding.
~~~
看出玄機了嗎?在函數中,本來有兩個print語句,但是中間插入了一個return,僅僅是一個return。當執行函數的時候,只執行了第一個print語句,第二個并沒有執行。這是因為第一個之后,遇到了return,它告訴函數要返回,即中斷函數體內的流程,離開這個函數。結果第二個print就沒有被執行。所以,return在這里就有了一個作用,結束正在執行的函數。有點類似循環中的break的作用。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#函數中的文檔)函數中的文檔
“程序在大多數情況下是給人看的,只是偶爾被機器執行以下。”所以,寫程序必須要寫注釋。前面已經有過說明,如果用`#`開始,python就不執行那句(python看不到它,但是人能看到),它就作為注釋存在。
除了這樣的一句之外,一般在每個函數名字的下面,還要寫一寫文檔,以此來說明這個函數的用途。
~~~
#!/usr/bin/env python
# coding=utf-8
def fibs(n):
"""
This is a Fibonacci sequence.
"""
result = [0,1]
for i in range(n-2):
result.append(result[-2] + result[-1])
return result
if __name__ == "__main__":
lst = fibs(10)
print lst
~~~
在這個函數的名稱下面,用三個引號的方式,包裹著對這個函數的說明,那個就是函數文檔。
還記得在[《自省》](https://github.com/qiwsir/StarterLearningPython/blob/master/130.md)那節中,提到的`__doc__`嗎?對于函數,它的內容就來自這里。
~~~
>>> def my_fun():
... """
... This is my function.
... """
... print "I am a craft."
...
>>> my_fun.__doc__
'\n This is my function.\n '
~~~
如果在交互模式中用`help(my_fun)`得到的也是三個引號所包裹的文檔信息。
~~~
Help on function my_fun in module __main__:
my_fun()
This is my function.
~~~
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#參數和變量)參數和變量
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#參數)參數
雖然在上一節,已經知道如何通過函數的參數傳值,如何調用函數等。但是,這里還有必要進一步討論參數問題。在別的程序員嘴里,你或許聽說過“形參”、“實參”、“參數”等名詞,到底指什么呢?
> 在定義函數的時候(def來定義函數,稱為def語句),函數名后面的括號里如果有變量,它們通常被稱為“形參”。調用函數的時候,給函數提供的值叫做“實參”,或者“參數”。
其實,根本不用區分這個,因為沒有什么意義,只不過類似孔乙己先生知道茴香豆的茴字有多少種寫法罷了。但是,我居然碰到過某公司面試官問這種問題。
**在本教程中,把那個所謂實參,就稱之為值(或者數據、或者對象),形參就籠統稱之為參數(似乎不很合理,但是接近數學概念)。**
### [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#比較參數和變量)比較參數和變量
參數問題就算說明白了,糊涂就糊涂吧,也沒有什么關系。不過,對于變量和參數,這兩個就不能算糊涂賬了。因為它容易讓人糊涂了。
在數學的函數中`y = 3x + 2`,那個x叫做參數,也可以叫做變量。但是,在編程語言的函數中,與此有異。
先參考一段來自[微軟網站](http://msdn.microsoft.com/zh-cn/library/9kewt1b3.aspx)的比較高度抽象,而且意義涵蓋深遠的說明。我摘抄過來,看官讀一讀,是否理解,雖然是針對VB而言的,一樣有啟發。
> 參數和變量之間的差異 (Visual Basic)
>
> 多數情況下,過程必須包含有關調用環境的一些信息。執行重復或共享任務的過程對每次調用使用不同的信息。此信息包含每次調用過程時傳遞給它的變量、常量和表達式。
>
> 若要將此信息傳遞給過程,過程先要定義一個形參,然后調用代碼將一個實參傳遞給所定義的形參。 您可以將形參當作一個停車位,而將實參當作一輛汽車。 就像一個停車位可以在不同時間停放不同的汽車一樣,調用代碼在每次調用過程時可以將不同的實參傳遞給同一個形參。
>
> 形參表示一個值,過程希望您在調用它時傳遞該值。
>
> 當您定義 Function 或 Sub 過程時,需要在緊跟過程名稱的括號內指定形參列表。對于每個形參,您可以指定名稱、數據類型和傳入機制(ByVal (Visual Basic) 或 ByRef (Visual Basic))。您還可以指示某個形參是可選的。這意味著調用代碼不必傳遞它的值。
>
> 每個形參的名稱均可作為過程內的局部變量。形參名稱的使用方法與其他任何變量的使用方法相同。
>
> 實參表示在您調用過程時傳遞給過程形參的值。調用代碼在調用過程時提供參數。
>
> 調用 Function 或 Sub 過程時,需要在緊跟過程名稱的括號內包括實參列表。每個實參均與此列表中位于相同位置的那個形參相對應。
>
> 與形參定義不同,實參沒有名稱。每個實參就是一個表達式,它包含零或多個變量、常數和文本。求值的表達式的數據類型通常應與為相應形參定義的數據類型相匹配,并且在任何情況下,該表達式值都必須可轉換為此形參類型。
看官如果硬著頭皮看完這段引文,發現里面有幾個關鍵詞:參數、變量、形參、實參。本來想弄清楚參數和變量,結果又冒出另外兩個東東,更混亂了。請稍安勿躁,本來這段引文就是有點多余,但是,之所以引用,就是讓列位開闊一下眼界,在編程業界,類似的東西有很多名詞。下次聽到有人說這些,不用害怕啦,反正自己聽過了。
在Python中,沒有這么復雜。
看完上面讓人暈頭轉向的引文之后,再看下面的代碼,就會豁然開朗了。
~~~
>>> def add(x): #x是參數,準確說是形參
... a = 10 #a是變量
... return a+x #x就是那個形參作為變量,其本質是要傳遞賦給這個函數的值
...
>>> x = 3 #x是變量,只不過在函數之外
>>> add(x) #這里的x是參數,但是它由前面的變量x傳遞對象3
13
>>> add(3) #把上面的過程合并了
13
~~~
至此,看官是否清楚了一點點。當然,我所表述不正確之處或者理解錯誤之處,也請看官不吝賜教,小可作揖感謝。
其實沒有那么復雜。關鍵要理解函數名括號后面的東東(管它什么參呢)的作用是傳遞值。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#全局變量和局部變量)全局變量和局部變量
下面是一段代碼,注意這段代碼中有一個函數funcx(),這個函數里面有一個變量x=9,在函數的前面也有一個變量x=2
~~~
x = 2
def funcx():
x = 9
print "this x is in the funcx:-->",x
funcx()
print "--------------------------"
print "this x is out of funcx:-->",x
~~~
那么,這段代碼輸出的結果是什么呢?看:
~~~
this x is in the funcx:--> 9
--------------------------
this x is out of funcx:--> 2
~~~
從輸出看出,運行funcx(),輸出了funcx()里面的變量x=9;然后執行代碼中的最后一行,print "this x is out of funcx:-->",x
特別要關注的是,前一個x輸出的是函數內部的變量x;后一個x輸出的是函數外面的變量x。兩個變量彼此沒有互相影響,雖然都是x。從這里看出,兩個x各自在各自的領域內起到作用。
把那個只在函數體內(某個范圍內)起作用的變量稱之為**局部變量**。
有局部,就有對應的全部,在漢語中,全部變量,似乎有歧義,幸虧漢語豐富,于是又取了一個名詞:**全局變量**
~~~
x = 2
def funcx():
global x #跟上面函數的不同之處
x = 9
print "this x is in the funcx:-->",x
funcx()
print "--------------------------"
print "this x is out of funcx:-->",x
~~~
以上兩段代碼的不同之處在于,后者在函數內多了一個`global x`,這句話的意思是在聲明x是全局變量,也就是說這個x跟函數外面的那個x同一個,接下來通過x=9將x的引用對象變成了9。所以,就出現了下面的結果。
~~~
this x is in the funcx:--> 9
--------------------------
this x is out of funcx:--> 9
~~~
好似全局變量能力很強悍,能夠統帥函數內外。但是,要注意,這個東西要慎重使用,因為往往容易帶來變量的換亂。內外有別,在程序中一定要注意的。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/202.md#命名空間)命名空間
這是一個比較不容易理解的概念,特別是對于初學者而言,似乎它很飄渺。我在維基百科中看到對它的定義,不僅定義比較好,連里面的例子都不錯。所以,抄錄下來,幫助讀者理解這個名詞。
> 命名空間(英語:Namespace)表示標識符(identifier)的可見范圍。一個標識符可在多個命名空間中定義,它在不同命名空間中的含義是互不相干的。這樣,在一個新的命名空間中可定義任何標識符,它們不會與任何已有的標識符發生沖突,因為已有的定義都處于其它命名空間中。
>
> 例如,設Bill是X公司的員工,工號為123,而John是Y公司的員工,工號也是123。由于兩人在不同的公司工作,可以使用相同的工號來標識而不會造成混亂,這里每個公司就表示一個獨立的命名空間。如果兩人在同一家公司工作,其工號就不能相同了,否則在支付工資時便會發生混亂。
>
> 這一特點是使用命名空間的主要理由。在大型的計算機程序或文檔中,往往會出現數百或數千個標識符。命名空間(或類似的方法,見“命名空間的模擬”一節)提供一隱藏區域標識符的機制。通過將邏輯上相關的標識符組織成相應的命名空間,可使整個系統更加模塊化。
>
> 在編程語言中,命名空間是對作用域的一種特殊的抽象,它包含了處于該作用域內的標識符,且本身也用一個標識符來表示,這樣便將一系列在邏輯上相關的標識符用一個標識符組織了起來。許多現代編程語言都支持命名空間。在一些編程語言(例如C++和Python)中,命名空間本身的標識符也屬于一個外層的命名空間,也即命名空間可以嵌套,構成一個命名空間樹,樹根則是無名的全局命名空間。
>
> 函數和類的作用域可被視作隱式命名空間,它們和可見性、可訪問性和對象生命周期不可分割的聯系在一起。
顯然,用“命名空間”或者“作用域”這樣的名詞,就是因為有了函數(后面還會有類)之后,在函數內外都可能有外形一樣的符號(標識符),在python中(乃至于其它高級語言),通常就是變量,為了區分此變量非彼變量(雖然外形一樣),需要用這樣的東西來框定每個變量所對應的值(發生作用的范圍)。
前面已經講過,變量和對象(就是所變量所對應的值)之間的關系是:變量類似標簽,貼在了對象上。也就是,通過賦值語句實現了一個變量標簽對應一個數據對象(值),這種對應關系讓你想起了什么?映射!python中唯一的映射就是dict,里面有“鍵值對”。變量和值得關系就有點像“鍵”和“值”的關系。有一個內建函數vars,可以幫助我們研究一下這種對應關系。
~~~
>>> x = 7
>>> scope = vars()
>>> scope['x']
7
>>> scope['x'] += 1
>>> x
8
>>> scope['x']
8
~~~
既然如此,誠如前面的全局變量和局部變量,即使是同樣一個變量名稱。但是它在不同范圍(還是用“命名空間”這個詞是不是更專業呢?)對應不同的值。
- 第零章 預備
- 關于Python的故事
- 從小工到專家
- Python安裝
- 集成開發環境
- 第壹章 基本數據類型
- 數和四則運算
- 除法
- 常用數學函數和運算優先級
- 寫一個簡單的程序
- 字符串(1)
- 字符串(2)
- 字符串(3)
- 字符串(4)
- 字符編碼
- 列表(1)
- 列表(2)
- 列表(3)
- 回顧list和str
- 元組
- 字典(1)
- 字典(2)
- 集合(1)
- 集合(2)
- 第貳章 語句和文件
- 運算符
- 語句(1)
- 語句(2)
- 語句(3)
- 語句(4)
- 語句(5)
- 文件(1)
- 文件(2)
- 迭代
- 練習
- 自省
- 第叁章 函數
- 函數(1)
- 函數(2)
- 函數(3)
- 函數(4)
- 函數練習
- 第肆章 類
- 類(1)
- 類(2)
- 類(3)
- 類(4)
- 類(5)
- 多態和封裝
- 特殊方法(1)
- 特殊方法(2)
- 迭代器
- 生成器
- 上下文管理器
- 第伍章 錯誤和異常
- 錯誤和異常(1)
- 錯誤和異常(2)
- 錯誤和異常(3)
- 第陸章 模塊
- 編寫模塊
- 標準庫(1)
- 標準庫(2)
- 標準庫(3)
- 標準庫(4)
- 標準庫(5)
- 標準庫(6)
- 標準庫(7)
- 標準庫(8)
- 第三方庫
- 第柒章 保存數據
- 將數據存入文件
- mysql數據庫(1)
- MySQL數據庫(2)
- mongodb數據庫(1)
- SQLite數據庫
- 電子表格
- 第捌章 用Tornado做網站
- 為做網站而準備
- 分析Hello
- 用tornado做網站(1)
- 用tornado做網站(2)
- 用tornado做網站(3)
- 用tornado做網站(4)
- 用tornado做網站(5)
- 用tornado做網站(6)
- 用tornado做網站(7)
- 第玖章 科學計算
- 為計算做準備
- Pandas使用(1)
- Pandas使用(2)
- 處理股票數據
- 附:網絡文摘
- 如何成為Python高手
- ASCII、Unicode、GBK和UTF-8字符編碼的區別聯系