Python 學習的第一個難關(guān) -- 函數(shù),這個地方學會的人覺得沒有啥,沒學過的學的時候迷迷瞪瞪,不用慌,學編程就是這樣,先學過去,在從上帝視角去看,堅持打卡,我一天寫一篇,你可以兩天學一篇。

九、Python 中函數(shù)是基礎部分第一道難關(guān)

9.1 函數(shù)出現(xiàn)的背景

為什么在編程語言中會出現(xiàn)函數(shù)概念,目的有 2 個。

  1. 在開發(fā)的時候,很多項目都是團隊開發(fā),把代碼功能編寫成一個個的函數(shù),方便維護,每個人也可相對獨立的開發(fā),縮短整體開發(fā)時間
  2. 代碼編寫成函數(shù)以后,重復的功能只寫一次即可,其它地方可以直接調(diào)用,方便了代碼的復用。

直接寫出這兩個目的還是沒有直觀的感受,要在代碼中感受函數(shù)的用法。

9.2 函數(shù)初接觸

咱從本系列博客一開始就已經(jīng)接觸函數(shù)的概念了,例如第一篇就學習的 print, 就是一個內(nèi)置函數(shù),相似的還有 len、addsorted 這些,調(diào)用函數(shù)時最大的便捷性就是不用知道函數(shù)內(nèi)部的具體實現(xiàn),就可以用該函數(shù)使用自己想要的效果。

9.2.1 函數(shù)的定義

具體的語法格式如下:

def 函數(shù)名稱(參數(shù)1[,參數(shù)2,參數(shù)3...]):    代碼塊    代碼塊    return 返回值

if 語句,for 語句一樣,函數(shù)定義與使用的時候要注意下代碼縮進。

函數(shù)名稱必須是唯一的,進行聲明的有意義,不要使用 a 當做函數(shù)名,以后很有可能就忘記了這個函數(shù)是做什么用的。

參數(shù)值,非必須,根據(jù)函數(shù)的要求自行設定即可,各個參數(shù)值之間用 , 分隔。

返回值,非必須,返回多個值使用逗號 , 分隔即可。

注意第一行末尾的分號

9.2.2 無參數(shù)無返回值的函數(shù)

該內(nèi)容將演示函數(shù)的使用便捷性。

# 創(chuàng)建一個函數(shù)def show():    print("我是一個無參數(shù)無返回值的函數(shù)")    print("hello world")show()show()show()show()show()

函數(shù)聲明之后,通過 函數(shù)名() 進行調(diào)用,上面的代碼寫了 5 次 show(),表示函數(shù)被調(diào)用了 5 次。

如果不通過函數(shù)實現(xiàn)上文效果,需要將函數(shù)內(nèi)部的代碼復制 5 次。

print("我是一個無參數(shù)無返回值的函數(shù)")print("hello world")print("我是一個無參數(shù)無返回值的函數(shù)")print("hello world")print("我是一個無參數(shù)無返回值的函數(shù)")print("hello world")print("我是一個無參數(shù)無返回值的函數(shù)")print("hello world")print("我是一個無參數(shù)無返回值的函數(shù)")print("hello world")

單純的復制代碼還好,如果我現(xiàn)在希望將 hello 修改成 hi ,那不使用函數(shù)需要修改代碼的 5 個位置,而使用函數(shù)只需要修改函數(shù)中的代碼塊一處即可。

9.3 函數(shù)的參數(shù)設計

上文中設計的是一個無參數(shù)的函數(shù),但在實際應用中很少出現(xiàn)一個無參函數(shù),更多的時候是需要給函數(shù)傳遞參數(shù)的。

9.3.1 傳遞一個參數(shù)

函數(shù)內(nèi)有參數(shù),代碼如下:

# 聲明一個帶一個參數(shù)的函數(shù)def show(name):    print("傳遞進來的姓名是:", name)show("橡皮擦")show("大橡皮擦")show("小橡皮擦")

小括號里面的 name 即為參數(shù),該參數(shù)能在函數(shù)內(nèi)部代碼塊直接使用。

9.3.2 傳遞多個參數(shù)

多個參數(shù)只需要在函數(shù)聲明時小括號里面多增加幾個參數(shù)即可。

# 聲明一個帶多參數(shù)的函數(shù)def show(name, age):    print("傳遞進來的姓名是:", name, " 年齡是:", age)show("橡皮擦", 20)show("大橡皮擦", 21)show("小橡皮擦", 18)

多個參數(shù)一定要注意參數(shù)的位置,前后順序如果錯誤,會導致嚴重 BUG。上述代碼中 橡皮擦 會傳遞給 name,20 會傳遞給 age

9.3.3 關(guān)鍵詞參數(shù)(參數(shù)名稱=值)

該參數(shù)使用的方式是在調(diào)用函數(shù)時,參數(shù)用 參數(shù)名稱=值 這種形式傳遞。其實在上述傳遞一個和多個參數(shù)的時候,也可以應用該方式傳遞參數(shù),例如修改代碼如下。

# 聲明一個帶一個參數(shù)的函數(shù)def show(name):    print("傳遞進來的姓名是:", name)show(name="橡皮擦")# 聲明一個帶多參數(shù)的函數(shù)def show1(name, age):    print("傳遞進來的姓名是:", name, " 年齡是:", age)show1(name="橡皮擦", age=20)

用這種形式調(diào)用函數(shù),參數(shù)的位置就不在重要了,因為已經(jīng)指定參數(shù)具體值是多少。

9.3.4 參數(shù)默認值

在定義函數(shù)的時候可以給參數(shù)一個默認值,如果調(diào)用該函數(shù)沒有給該參數(shù)賦值,程序會使用默認值而不會報錯。

# 沒有默認值的參數(shù)def show(name):    print("傳遞進來的姓名是:", name)show() # 該函數(shù)調(diào)用時會報錯# 有默認值的參數(shù)def show(name="橡皮擦"):    print("傳遞進來的姓名是:", name)show() # 該函數(shù)調(diào)用沒有問題,即使沒有傳遞參數(shù)會使用默認值

如果一個參數(shù)有默認值,注意該參數(shù)必須放在函數(shù)參數(shù)位置的最右側(cè),例如下述函數(shù)定義。

def show(a,b,c,name="橡皮擦"):    pass

9.4 函數(shù)返回值

函授的返回值屬于非必選項,可以寫可以不寫,不寫的時候也會有返回值,該值為 None。

9.4.1 返回 None

函數(shù)如果沒有寫返回值即 return,那 Python 會自動在函數(shù)體內(nèi)增加一行代碼 return None。函數(shù)的返回值可以賦值給一個變量,通過打印該變量,即可知道返回的具體內(nèi)容。

# 沒有返回值的函數(shù)def show():    print("注意下面沒有 return")ret = show()print(ret)

得到的 ret 的值是 None,表示沒有返回值。

如果只寫 return,也會返回 None,注意下述代碼是正確的。

# 沒有返回值的函數(shù)def show():    print("注意下面沒有 return")    returnret = show()print(ret)

9.4.2 返回一個值

函數(shù)常見的情況是有返回值的,例如執(zhí)行一段計算代碼之后返回計算結(jié)果。

# 定義一個減法函數(shù)def sub(a, b):    c = a - b    return c# 參數(shù)為 2 和 1,將結(jié)果進行返回ret = sub(2, 1)print(ret)

9.4.3 返回多個值

使用 return 返回函數(shù)數(shù)據(jù),可以一次性返回多個值,返回的數(shù)據(jù)之間用逗號分隔即可。

# 定義一個減法函數(shù)def sub(a, b):    c = a - b    d = a + b    f = a / b    return c, d, f# 參數(shù)為 2 和 1,將結(jié)果進行返回ret = sub(2, 1)print(ret)

返回的結(jié)果是一個元組 (1, 3, 2.0)。

9.4.4 返回字典與列表

函數(shù)可以返回字符串類型的變量,這個與返回一個值一樣,只是具體數(shù)據(jù)類型的區(qū)別,除了上述提及的內(nèi)容,函數(shù)也可以返回比較復雜的數(shù)據(jù),例如字典或者列表,該內(nèi)容沒有特別說明的,你只需要將字典和列表看成普通的數(shù)據(jù)類型進行返回即可,例如下述代碼。

def sub1():    return [1, 2, 3]def sub2():    return {"name": "橡皮擦", "loc": "CSDN"}

9.5 調(diào)用函數(shù)時參數(shù)是列表

為什么多帶帶將其參數(shù)是列表時拿出來講解,是因為列表這個有點特殊,里面還會引出全局變量與局部變量的一個概念,放心第一遍學習 100%迷糊。如果你有其他編程語言的功底,那另當別論。

具體代碼如下,注意看出現(xiàn)的問題。

names = ["aaa","bbb","ccc"]def change_list(one_names):    # 修改傳遞進來的列表索引 0 的位置上為 jjj    one_names[0] = "jjj"# 函數(shù)調(diào)用,同時將 name 作為參數(shù)傳遞進函數(shù)內(nèi)部change_list(names)print(names)

最終的輸出結(jié)果是 [jjj, bbb, ccc],這個表示啥?表示函數(shù)外面的 names 被函數(shù)給修改了。疑問是所有在函數(shù)外面的變量傳遞到參數(shù)內(nèi)部都會被修改嗎?換個整數(shù)試試。

score = 1def change_list(one_score):    one_score = 333# 函數(shù)調(diào)用,同時將 score 作為參數(shù)傳遞進函數(shù)內(nèi)部change_list(score)print(score)

此時 score 雖然在函數(shù)內(nèi)部被修改為了 333,但是在函數(shù)外打印并沒有被修改,還是 1?,F(xiàn)在問題就出現(xiàn)了,Python 并不是一視同仁的,列表變量在函數(shù)內(nèi)被修改影響到了外部變量,而整型變量并沒有受到影響。

這個坑咱先挖下,以后慢慢填,為啥列表在函數(shù)內(nèi)修改,函數(shù)外面也會受到影響,涉及到內(nèi)存地址空間這些更底層的概念了。先學習一下局部變量與全局變量。

9.6 局部變量與全局變量

這個概念非常難理解,是的,如果你理解了在這個階段也是一知半解。

在設計函數(shù)的時候,有時需要控制變量的使用范圍,如果變量的使用范圍在函數(shù)內(nèi)部,那這個變量叫做局部變量,注意是函數(shù)內(nèi)部。如果某個變量的使用范圍在整個程序,這個變量就是全局變量。

上述內(nèi)容只是初次接觸,在函數(shù)部分咱先簡單弄個概念在心里在說。

全局變量在所有函數(shù)都能用

score = 1def change_list():    # 輸出全局變量 score,因為這個變量在函數(shù)外面聲明的,大家都可以用    print(score)change_list()# 在函數(shù)外面也可以使用print(score)

上面的 score 并未在函數(shù)內(nèi)部聲明,但是函數(shù)內(nèi)部也可以訪問到,那函數(shù)外面的變量 score 就是全局變量。

局部變量在函數(shù)外部和其它函數(shù)不能使用

def change_list():    # 局部變量 score,本函數(shù)可以用    score = 1    print(score)change_list()# 局部變量 score,在函數(shù)外面不可以使用print(score)# 其它函數(shù)內(nèi)部也不可以使用def show():    print(score)

如果局部變量和全局變量名稱重復
在程序設計的時候很容易出現(xiàn)重名的情況,這種情況比較尷尬了,初學難度繼續(xù)增加。

score = 5555def change_list():    # 局部變量 score,本函數(shù)可以用    score = 6666    print(score)change_list()# 外面使用的全局變量 scoreprint(score)

執(zhí)行之后發(fā)現(xiàn)在外面使用的是全局變量的值 5555,在函數(shù)內(nèi)部使用的局部變量的值 6666。

有了上面的這些基礎知識點,你應該注意下變量的使用范圍這個概念了,在面向?qū)ο蟮臅r候還將繼續(xù)學習。

9.7 傳遞任意數(shù)量的參數(shù)

9.7.1 一般參數(shù)與任意參數(shù)的組合

在 Python 編寫代碼的過程中,很容易出現(xiàn)一種情況是你不知道有多少個參數(shù),這時函數(shù)定義時參數(shù)就不好設定了,好在 Python 已經(jīng)想到了這個情況。

def show(*keys):    print("傳入的參數(shù)可以循環(huán)打印")    for key in keys:        print(key)show(1,2,3,4,5,6,7)

在函數(shù)定義的時候,參數(shù)位置使用 *參數(shù)名,然后在函數(shù)體重的代碼塊位置就可以進行循環(huán)打印了,可以捕獲到任意數(shù)量的參數(shù)。

如果又有一般參數(shù),又有不定數(shù)量的參數(shù)怎么辦?好辦,使用下面的格式。

def show(name,age,*keys):    print("傳入的參數(shù)可以循環(huán)打印")    print(name)    print(age)    for key in keys:        print(key)show("www",18,1,2,3)

希望你現(xiàn)在發(fā)現(xiàn)規(guī)律,函數(shù)調(diào)用的時候先依次去匹配函數(shù)定義的參數(shù),一一對應,都對應完畢,發(fā)現(xiàn)沒有一般參數(shù)了,剩下的都打包給 *keys 就好了。

還要不要寫兩個帶 * 的參數(shù),例如 def show(name,*keys,*keys1) ,這樣就報錯了。

9.7.2 一般參數(shù)與任意數(shù)量的關(guān)鍵詞參數(shù)

參數(shù)里面還有一種情況是需要傳不定數(shù)量的關(guān)鍵詞參數(shù),這又如何設計呢?

def show(**args):    print("傳入的參數(shù)可以循環(huán)打印")    for key in args.items():        print(key)show(name="橡皮擦", age=18)

這種情況,你把傳遞進去的所有參數(shù)當成一個字典即可,自動聚合成了一個字典類型的數(shù)據(jù)。

如果與一般參數(shù)進行匹配,規(guī)則也非常簡單。

def show(name,**args):    print("傳入的參數(shù)可以循環(huán)打印")    print(name)    for key in args.items():        print(key)show(name="橡皮擦", age=18)

上述代碼先去匹配關(guān)鍵詞參數(shù),匹配成功了就賦值到對應關(guān)鍵詞上,例如上面的 name 變量在函數(shù)調(diào)用時賦值了 橡皮擦,那它就等于橡皮擦,其余的例如 age 沒有關(guān)鍵詞參數(shù)對應,只好打包給 **args 了。

9.8 函數(shù)以后的擴展

函數(shù)本文只是介紹了 5 成左右的內(nèi)容,在 Python 中函數(shù)還有很多有趣的知識點存在,但是不能一口吃飽,那樣的結(jié)果就是消化不好,而且不符合咱滾雪球?qū)W Python 的設計初衷,就是要滾上幾遍才可以徹底掌握。

延期學習的知識點如下:

  • 遞歸函數(shù)
  • 匿名函數(shù)
  • 高階函數(shù)

還有一個知識點你可以現(xiàn)在擴展,咱學習了各種參數(shù)的形式,你有沒有想過將這些組合在一起呢,例如 def show(name,age,*arg,**args) 在程序中是怎么解析的呢?順序變動可不可以呢?

def show(name, age, sex="男", *arg, **args):    print("傳入的參數(shù)可以循環(huán)打印")    print(name)    for key in args.items():        print(key)show("橡皮擦", 18, "女", like=99)

9.9 這篇博客的總結(jié)

函數(shù),初學階段,這是啥,為什么,不懂唉;學習中期,寫函數(shù)好舒服啊,面向?qū)ο竽抢镉泻瘮?shù)好用;學習后期,快點實現(xiàn)這個功能,啥方法都行,抓緊點,馬上下班了。

沒錯,函數(shù)就處在一個說難很難,轉(zhuǎn)身又很簡單的一個環(huán)節(jié)。

最后一碗毒雞湯

編程需要一個頓悟的時刻,而這個時刻很多人就沒有過。 O(∩_∩)O 哈哈~

????????????????????????????


今天是持續(xù)寫作的第 8 / 100 天。
如果你有想要交流的想法、技術(shù),歡迎在評論區(qū)留言。