函數(shù)(1)

2018-02-24 15:48 更新

函數(shù),對(duì)于人類來講,能夠發(fā)展到這個(gè)數(shù)學(xué)思維層次,是一個(gè)飛躍??梢哉f,它的提出,直接加快了現(xiàn)代科技和社會(huì)的發(fā)展,不論是現(xiàn)代的任何科技門類,乃至于經(jīng)濟(jì)學(xué)、政治學(xué)、社會(huì)學(xué)等,都已經(jīng)普遍使用函數(shù)。

下面一段來自維基百科(在本教程中,大量的定義來自維基百科,因?yàn)樗娴暮馨倏疲?a rel="external nofollow" target="_blank" target="_blank">函數(shù)詞條

函數(shù)這個(gè)數(shù)學(xué)名詞是萊布尼茲在1694年開始使用的,以描述曲線的一個(gè)相關(guān)量,如曲線的斜率或者曲線上的某一點(diǎn)。萊布尼茲所指的函數(shù)現(xiàn)在被稱作可導(dǎo)函數(shù),數(shù)學(xué)家之外的普通人一般接觸到的函數(shù)即屬此類。對(duì)于可導(dǎo)函數(shù)可以討論它的極限和導(dǎo)數(shù)。此兩者描述了函數(shù)輸出值的變化同輸入值變化的關(guān)系,是微積分學(xué)的基礎(chǔ)。

中文的“函數(shù)”一詞由清朝數(shù)學(xué)家李善蘭譯出。其《代數(shù)學(xué)》書中解釋:“凡此變數(shù)中函(包含)彼變數(shù)者,則此為彼之函數(shù)”。

函數(shù),從簡(jiǎn)單到復(fù)雜,各式各樣。前面提供的維基百科中的函數(shù)詞條,里面可以做一個(gè)概覽。但不管什么樣子的函數(shù),都可以用下圖概括:

有初中數(shù)學(xué)水平都能理解一個(gè)大概了。這里不贅述。

本講重點(diǎn)說明用python怎么來構(gòu)造一個(gè)函數(shù)。

深入理解函數(shù)

在中學(xué)數(shù)學(xué)中,可以用這樣的方式定義函數(shù):y=4x+3,這就是一個(gè)一次函數(shù),當(dāng)然,也可以寫成:f(x)=4x+3。其中x是變量,它可以代表任何數(shù)。

當(dāng)x=2時(shí),代入到上面的函數(shù)表達(dá)式:
f(2) = 4*2+3 = 11
所以:f(2) = 11

但是,這并不是函數(shù)的全部,在函數(shù)中,其實(shí)變量并沒有規(guī)定只能是一個(gè)數(shù),它可以是饅頭、還可是蘋果,不知道讀者是否對(duì)函數(shù)有這個(gè)層次的理解。請(qǐng)繼續(xù)閱讀即更深刻

變量不僅僅是數(shù)

變量x只能是任意數(shù)嗎?其實(shí),一個(gè)函數(shù),就是一個(gè)對(duì)應(yīng)關(guān)系。看官嘗試著將上面表達(dá)式的x理解為餡餅,4x+3,就是4個(gè)餡餅在加上3(一般來講,單位是統(tǒng)一的,但你非讓它不統(tǒng)一,也無妨),這個(gè)結(jié)果對(duì)應(yīng)著另外一個(gè)東西,那個(gè)東西比如說是iphone?;蛘哒f可以理解為4個(gè)餡餅加3就對(duì)應(yīng)一個(gè)iphone。這就是所謂映射關(guān)系。

所以,x,不僅僅是數(shù),可以是你認(rèn)為的任何東西。

變量本質(zhì)——占位符

函數(shù)中為什么變量用x?這是一個(gè)有趣的問題,自己google一下,看能不能找到答案。

我也不清楚原因。不過,我清楚地知道,變量可以用x,也可以用別的符號(hào),比如y,z,k,i,j...,甚至用alpha,beta這樣的字母組合也可以。

變量在本質(zhì)上就是一個(gè)占位符。這是一針見血的理解。什么是占位符?就是先把那個(gè)位置用變量占上,表示這里有一個(gè)東西,至于這個(gè)位置放什么東西,以后再說,反正先用一個(gè)符號(hào)占著這個(gè)位置(占位符)。

其實(shí)在高級(jí)語言編程中,變量比我們?cè)诔踔袛?shù)學(xué)中學(xué)習(xí)的要復(fù)雜。但是,先不管那些,復(fù)雜東西放在以后再說了?,F(xiàn)在,就按照初中數(shù)學(xué)的水平來研究python中的變量。

通常使小寫字母來命名python中的變量,也可以在其中加上下劃線什么的,表示區(qū)別。

比如:alpha,x,j,p_beta,這些都可以做為python的變量。

建立簡(jiǎn)單函數(shù)

>>> a = 2
>>> y = 3 * a + 2
>>> y
8

這種方式建立的函數(shù),跟在初中數(shù)學(xué)中學(xué)習(xí)的沒有什么區(qū)別。當(dāng)然,這種方式的函數(shù),在編程實(shí)踐中沒有什么用途。

別急躁,你在輸入a=3,然后輸入y,看看得到什么結(jié)果呢?

>>> a = 2
>>> y = 3 * a + 2
>>> y
8
>>> a = 3
>>> y
8

是不是很奇怪?為什么后面已經(jīng)讓a等于3了,結(jié)果y還是8。

還記得前面已經(jīng)學(xué)習(xí)過的關(guān)于“變量賦值”的原理嗎?a=2的含義是將2這個(gè)對(duì)象貼上了變量a標(biāo)簽,經(jīng)過計(jì)算,得到了8,之后變量y引用了對(duì)象8。當(dāng)變量a引用的對(duì)象修改為3的時(shí)候,但是y引用的對(duì)象還沒有變,所以,還是8。再計(jì)算一次,y的連接對(duì)象就變了:

>>> a = 3
>>> y
8
>>> y = 3 * a + 2
>>> y
11

特別注意,如果沒有先 a = 2 ,就直接下函數(shù)表達(dá)式了,像這樣,就會(huì)報(bào)錯(cuò)。

>>> y = 3 * a + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  NameError: name 'a' is not defined

注意看錯(cuò)誤提示,a是一個(gè)變量,提示中告訴我們這個(gè)變量沒有定義。顯然,如果函數(shù)中要使用某個(gè)變量,不得不提前定義出來。定義方法就是給這個(gè)變量賦值。

建立實(shí)用的函數(shù)

上面用命令方式建立函數(shù),還不夠“正規(guī)化”,那么就來寫一個(gè).py文件吧。

例如下面的代碼:

#!/usr/bin/env python
#coding:utf-8

def add_function(a, b):
    c = a + b
    print c

if __name__ == "__main__":
    add_function(2, 3)

然后將文件保存,我把她命名為20101.py,你根據(jù)自己的喜好取個(gè)名字。

然后我就進(jìn)入到那個(gè)文件夾,運(yùn)行這個(gè)文件,出現(xiàn)下面的結(jié)果,如圖:

你運(yùn)行的結(jié)果是什么?如果沒有得到上面的結(jié)果,你就非常認(rèn)真地檢查代碼,是否跟我寫的完全一樣,注意,包括冒號(hào)和空格,都得一樣。冒號(hào)和空格很重要。

下面開始庖丁解牛:

  • def add_function(a, b): 這里是函數(shù)的開始。在聲明要建立一個(gè)函數(shù)的時(shí)候,一定要使用def(def 就是英文define的前三個(gè)字母),意思就是告知計(jì)算機(jī),這里要聲明一個(gè)函數(shù);add_function是這個(gè)函數(shù)名稱,取名字是有講究的,就好比你的名字一樣。在python中取名字的講究就是要有一定意義,能夠從名字中看出這個(gè)函數(shù)是用來干什么的。從add_function這個(gè)名字中,是不是看出她是用來計(jì)算加法的呢(嚴(yán)格地說,是把兩個(gè)對(duì)象“相加”,這里相加的含義是比較寬泛的,包括對(duì)字符串等相加)?(a,b)這個(gè)括號(hào)里面的是這個(gè)函數(shù)的參數(shù),也就是函數(shù)變量。冒號(hào),這個(gè)冒號(hào)非常非常重要,如果少了,就報(bào)錯(cuò)了。冒號(hào)的意思就是下面好開始真正的函數(shù)內(nèi)容了。
  • c = a + b?特別注意,這一行比上一行要縮進(jìn)四個(gè)空格。這是python的規(guī)定,要牢記,不可丟掉,丟了就報(bào)錯(cuò)。然后這句話就是將兩個(gè)參數(shù)(變量)相加,結(jié)果賦值與另外一個(gè)變量c。
  • print c?還是提醒看官注意,縮進(jìn)四個(gè)空格。將得到的結(jié)果c的值打印出來。
  • if __name__ == "__main__": 這句話先照抄,不解釋,因?yàn)樵?a href="http://hgci.cn/targetlink?url=https://github.com/qiwsir/StarterLearningPython/blob/master/130.md" target="_blank">《自省》有說明,不知道你是不是認(rèn)真閱讀了。注意就是不縮進(jìn)了。
  • add_function(2,3) 這才是真正調(diào)用前面建立的函數(shù),并且傳入兩個(gè)參數(shù):a=2,b=3。仔細(xì)觀察傳入?yún)?shù)的方法,就是把2放在a那個(gè)位置,3放在b那個(gè)位置(所以說,變量就是占位符).

解牛完畢,做個(gè)總結(jié):

定義函數(shù)的格式為:

def 函數(shù)名(參數(shù)1,參數(shù)2,...,參數(shù)n):

    函數(shù)體(語句塊)

是不是樣式很簡(jiǎn)單呢?

幾點(diǎn)說明:

  • 函數(shù)名的命名規(guī)則要符合python中的命名要求。一般用小寫字母和單下劃線、數(shù)字等組合
  • def是定義函數(shù)的關(guān)鍵詞,這個(gè)簡(jiǎn)寫來自英文單詞define
  • 函數(shù)名后面是圓括號(hào),括號(hào)里面,可以有參數(shù)列表,也可以沒有參數(shù)
  • 千萬不要忘記了括號(hào)后面的冒號(hào)
  • 函數(shù)體(語句塊),相對(duì)于def縮進(jìn),按照python習(xí)慣,縮進(jìn)四個(gè)空格

看簡(jiǎn)單例子,深入理解上面的要點(diǎn):

>>> def name():         #定義一個(gè)無參數(shù)的函數(shù),只是通過這個(gè)函數(shù)打印
...     print "qiwsir"  #縮進(jìn)4個(gè)空格
... 
>>> name()              #調(diào)用函數(shù),打印結(jié)果
qiwsir

>>> def add(x,y):       #定義一個(gè)非常簡(jiǎn)單的函數(shù)
...     return x+y      #縮進(jìn)4個(gè)空格
... 
>>> add(2,3)            #通過函數(shù),計(jì)算2+3
5

注意上面的add(x,y)函數(shù),在這個(gè)函數(shù)中,沒有特別規(guī)定參數(shù)x,y的類型。其實(shí),這句話本身就是錯(cuò)的,還記得在前面已經(jīng)多次提到,在python中,變量無類型,只有對(duì)象才有類型,這句話應(yīng)該說成:x,y并沒有嚴(yán)格規(guī)定其所引用的對(duì)象類型。這是python跟某些語言比如java很大的區(qū)別,在有些語言中,需要在定義函數(shù)的時(shí)候告訴函數(shù)參數(shù)的數(shù)據(jù)類型。python不用那樣做。

為什么?列位不要忘記了,這里的所謂參數(shù),跟前面說的變量,本質(zhì)上是一回事。只有當(dāng)用到該變量的時(shí)候,才建立變量與對(duì)象的對(duì)應(yīng)關(guān)系,否則,關(guān)系不建立。而對(duì)象才有類型。那么,在add(x,y)函數(shù)中,x,y在引用對(duì)象之前,是完全飄忽的,沒有被貼在任何一個(gè)對(duì)象上,換句話說它們有可能引用任何對(duì)象,只要后面的運(yùn)算許可,如果后面的運(yùn)算不許可,則會(huì)報(bào)錯(cuò)。

>>> add("qiw","sir")    #這里,x="qiw",y="sir",讓函數(shù)計(jì)算x+y,也就是"qiw"+"sir"
'qiwsir'

>>> add("qiwsir",4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in add
TypeError: cannot concatenate 'str' and 'int' objects  #仔細(xì)閱讀報(bào)錯(cuò)信息,就明白錯(cuò)誤之處了

從實(shí)驗(yàn)結(jié)果中發(fā)現(xiàn):x+y的意義完全取決于對(duì)象的類型。在python中,將這種依賴關(guān)系,稱之為多態(tài)。對(duì)于python中的多態(tài)問題,以后還會(huì)遇到,這里僅僅以此例子顯示一番。請(qǐng)看官要留心注意的:python中為對(duì)象編寫接口,而不是為數(shù)據(jù)類型。讀者先留心一下這句話,或者記住它,隨著學(xué)習(xí)的深入,會(huì)領(lǐng)悟到其真諦的。

此外,也可以將函數(shù)通過賦值語句,與某個(gè)變量建立引用關(guān)系:

>>> result = add(3,4)
>>> result
7

在這里,其實(shí)解釋了函數(shù)的一個(gè)秘密。add(x,y)在被運(yùn)行之前,計(jì)算機(jī)內(nèi)是不存在的,直到代碼運(yùn)行到這里的時(shí)候,在計(jì)算機(jī)中,就建立起來了一個(gè)對(duì)象,這就如同前面所學(xué)習(xí)過的字符串、列表等類型的對(duì)象一樣,運(yùn)行add(x,y)之后,也建立了一個(gè)add(x,y)的對(duì)象,這個(gè)對(duì)象與變量result可以建立引用關(guān)系,并且add(x,y)將運(yùn)算結(jié)果返回。于是,通過result就可以查看運(yùn)算結(jié)果。

如果看官上面一段,感覺有點(diǎn)吃力或者暈乎,也不要緊,那就再讀一邊。是在搞不明白,就不要搞了。隨著學(xué)習(xí)的深入,它會(huì)被明白的。

關(guān)于命名

到現(xiàn)在為止,我們已經(jīng)接觸過變量的命名、函數(shù)的命名問題。似乎已經(jīng)到了將命名問題進(jìn)行總結(jié)的時(shí)候了。

在某國(guó),向來重視“名”,所謂“名不正言不順”,取名字或者給什么東西命名,常常是天大的事情,在很多時(shí)候就是為了那個(gè)“名”進(jìn)行爭(zhēng)斗。

江湖上還有的大師,會(huì)通過某個(gè)人的名字來預(yù)測(cè)他/她的吉兇禍福等??磥砻诌@玩意太重要了?!懊徽圆豁槨?,歪解:名字不正規(guī)化,就不順。這是歪解,希望不要影響看官正確理解。不知道大師們是不是能夠通過外國(guó)人名字預(yù)測(cè)外國(guó)人大的吉兇禍福呢?比如Aoi sola,這個(gè)人怎么樣?不管怎樣,某國(guó)人是很在意名字的,旁邊有個(gè)國(guó)家似乎就不在乎,比如山本五十六,在名字中間出現(xiàn)數(shù)字,就好像我們的張三李四王二麻子那樣隨便,不過,有一種說法,“山本五十六”的意思是這個(gè)人出生時(shí),他父親56歲,看來跟張三還不一樣的。

python也很在乎名字問題,其實(shí),所有高級(jí)語言對(duì)名字都有要求。為什么呢?因?yàn)槿绻麃y了,計(jì)算機(jī)就有點(diǎn)不知所措了??磒ython對(duì)命名的一般要求。

  • 文件名:全小寫,可使用下劃線

  • 函數(shù)名:小寫,可以用下劃線風(fēng)格單詞以增加可讀性。如:myfunction,my_example_function。注意:混合大小寫僅被允許用于這種風(fēng)格已經(jīng)占據(jù)優(yōu)勢(shì)的時(shí)候,以便保持向后兼容。有的人,喜歡用這樣的命名風(fēng)格:myFunction,除了第一個(gè)單詞首字母外,后面的單詞首字母大寫。這也是可以的,因?yàn)樵谀承┱Z言中就習(xí)慣如此。

  • 函數(shù)的參數(shù):如果一個(gè)函數(shù)的參數(shù)名稱和保留的關(guān)鍵字(所謂保留關(guān)鍵字,就是python語言已經(jīng)占用的名稱,通常被用來做為已經(jīng)有的函數(shù)等的命名了,你如果還用,就不行了。)沖突,通常使用一個(gè)后綴下劃線好于使用縮寫或奇怪的拼寫。

  • 變量:變量名全部小寫,由下劃線連接各個(gè)單詞。如color = WHITE,this_is_a_variable = 1。

其實(shí),關(guān)于命名的問題,還有不少爭(zhēng)論呢?最典型的是所謂匈牙利命名法、駝峰命名等。如果你喜歡,可以google一下。以下內(nèi)容供參考:

調(diào)用函數(shù)

前面的例子中已經(jīng)有了一些關(guān)于調(diào)用的問題,為了深入理解,把這個(gè)問題單獨(dú)拿出來看看。

為什么要寫函數(shù)?從理論上說,不用函數(shù),也能夠編程,我們?cè)谇懊嬉呀?jīng)寫了程序,就沒有寫函數(shù),當(dāng)然,用python的內(nèi)建函數(shù)姑且不算了?,F(xiàn)在之所以使用函數(shù),主要是:

  1. 降低編程的難度,通常將一個(gè)復(fù)雜的大問題分解成一系列更簡(jiǎn)單的小問題,然后將小問題繼續(xù)劃分成更小的問題,當(dāng)問題細(xì)化為足夠簡(jiǎn)單時(shí),就可以分而治之。為了實(shí)現(xiàn)這種分而治之的設(shè)想,就要通過編寫函數(shù),將各個(gè)小問題逐個(gè)擊破,再集合起來,解決大的問題。(看官請(qǐng)注意,分而治之的思想是編程的一個(gè)重要思想,所謂“分治”方法也。)
  2. 代碼重(chong,二聲音)用。在編程的過程中,比較忌諱同樣一段代碼不斷的重復(fù),所以,可以定義一個(gè)函數(shù),在程序的多個(gè)位置使用,也可以用于多個(gè)程序。當(dāng)然,后面我們還會(huì)講到“模塊”(此前也涉及到了,就是import導(dǎo)入的那個(gè)東西),還可以把函數(shù)放到一個(gè)模塊中供其他程序員使用。也可以使用其他程序員定義的函數(shù)(比如import ...,前面已經(jīng)用到了,就是應(yīng)用了別人——?jiǎng)?chuàng)造python的人——寫好的函數(shù))。這就避免了重復(fù)勞動(dòng),提供了工作效率。

這樣看來,函數(shù)還是很必要的了。廢話少說,那就看函數(shù)怎么調(diào)用吧。以add(x,y)為例,前面已經(jīng)演示了基本調(diào)用方式,此外,還可以這樣:

>>> def add(x,y):       #為了能夠更明了顯示參數(shù)賦值特點(diǎn),重寫此函數(shù)
...     print "x=",x    #分別打印參數(shù)賦值結(jié)果
...     print "y=",y
...     return x+y
... 
>>> add(10,3)           #x=10,y=3
x= 10
y= 3
13

>>> add(3,10)           #x=3,y=10
x= 3
y= 10
13

所謂調(diào)用,最關(guān)鍵是要弄清楚如何給函數(shù)的參數(shù)賦值。這里就是按照參數(shù)次序賦值,根據(jù)參數(shù)的位置,值與之對(duì)應(yīng)。

>>> add(x=10,y=3)       #同上
x= 10
y= 3
13

還可以直接把賦值語句寫到里面,就明確了參數(shù)和對(duì)象的關(guān)系。當(dāng)然,這時(shí)候順序就不重要了,也可以這樣

>>> add(y=10,x=3)       #x=3,y=10
x= 3
y= 10
13

在定義函數(shù)的時(shí)候,參數(shù)可以像前面那樣,等待被賦值,也可以定義的時(shí)候就賦給一個(gè)默認(rèn)值。例如:

>>> def times(x,y=2):       #y的默認(rèn)值為2
...     print "x=",x
...     print "y=",y
...     return x*y
... 
>>> times(3)                #x=3,y=2
x= 3
y= 2
6

>>> times(x=3)              #同上
x= 3
y= 2
6

如果不給那個(gè)有默認(rèn)值的參數(shù)傳遞值(賦值的另外一種說法),那么它就是用默認(rèn)的值。如果給它傳一個(gè),它就采用新賦給它的值。如下:

>>> times(3,4)              #x=3,y=4,y的值不再是2
x= 3
y= 4
12

>>> times("qiwsir")         #再次體現(xiàn)了多態(tài)特點(diǎn)
x= qiwsir
y= 2
'qiwsirqiwsir'

給列位看官提一個(gè)思考題,請(qǐng)?jiān)陂e暇之余用python完成:寫兩個(gè)數(shù)的加、減、乘、除的函數(shù),然后用這些函數(shù),完成簡(jiǎn)單的計(jì)算。

注意事項(xiàng)

下面的若干條,是常見編寫代碼的注意事項(xiàng):

  1. 別忘了冒號(hào)。一定要記住符合語句首行末尾輸入“:”(if,while,for等的第一行)
  2. 從第一行開始。要確定頂層(無嵌套)程序代碼從第一行開始。
  3. 空白行在交互模式提示符下很重要。模塊文件中符合語句內(nèi)的空白行常被忽視。但是,當(dāng)你在交互模式提示符下輸入代碼時(shí),空白行則是會(huì)結(jié)束語句。
  4. 縮進(jìn)要一致。避免在塊縮進(jìn)中混合制表符和空格。
  5. 使用簡(jiǎn)潔的for循環(huán),而不是while or range.相比,for循環(huán)更易寫,運(yùn)行起來也更快
  6. 要注意賦值語句中的可變對(duì)象。
  7. 不要期待在原處修改的函數(shù)會(huì)返回結(jié)果,比如list.append(),這在可修改的對(duì)象中特別注意
  8. 調(diào)用函數(shù)是,函數(shù)名后面一定要跟隨著括號(hào),有時(shí)候括號(hào)里面就是空空的,有時(shí)候里面放參數(shù)。
  9. 不要在導(dǎo)入和重載中使用擴(kuò)展名或路徑。

以上各點(diǎn)如果有不理解的,也不要緊,在以后編程中,時(shí)不時(shí)地回來復(fù)習(xí)一下,能不斷領(lǐng)悟其內(nèi)涵。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)