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

九、Python 中函數是基礎部分第一道難關

9.1 函數出現的背景

為什么在編程語言中會出現函數概念,目的有 2 個。

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

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

9.2 函數初接觸

咱從本系列博客一開始就已經接觸函數的概念了,例如第一篇就學習的 print, 就是一個內置函數,相似的還有 lenadd、sorted 這些,調用函數時最大的便捷性就是不用知道函數內部的具體實現,就可以用該函數使用自己想要的效果。

9.2.1 函數的定義

具體的語法格式如下:

</>復制代碼

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

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

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

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

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

注意第一行末尾的分號

9.2.2 無參數無返回值的函數

該內容將演示函數的使用便捷性。

</>復制代碼

  1. # 創建一個函數def show(): print("我是一個無參數無返回值的函數") print("hello world")show()show()show()show()show()

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

如果不通過函數實現上文效果,需要將函數內部的代碼復制 5 次。

</>復制代碼

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

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

9.3 函數的參數設計

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

9.3.1 傳遞一個參數

函數內有參數,代碼如下:

</>復制代碼

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

小括號里面的 name 即為參數,該參數能在函數內部代碼塊直接使用。

9.3.2 傳遞多個參數

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

</>復制代碼

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

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

9.3.3 關鍵詞參數(參數名稱=值)

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

</>復制代碼

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

用這種形式調用函數,參數的位置就不在重要了,因為已經指定參數具體值是多少。

9.3.4 參數默認值

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

</>復制代碼

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

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

</>復制代碼

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

9.4 函數返回值

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

9.4.1 返回 None

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

</>復制代碼

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

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

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

</>復制代碼

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

9.4.2 返回一個值

函數常見的情況是有返回值的,例如執行一段計算代碼之后返回計算結果。

</>復制代碼

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

9.4.3 返回多個值

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

</>復制代碼

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

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

9.4.4 返回字典與列表

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

</>復制代碼

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

9.5 調用函數時參數是列表

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

具體代碼如下,注意看出現的問題。

</>復制代碼

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

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

</>復制代碼

  1. score = 1def change_list(one_score): one_score = 333# 函數調用,同時將 score 作為參數傳遞進函數內部change_list(score)print(score)

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

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

9.6 局部變量與全局變量

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

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

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

全局變量在所有函數都能用

</>復制代碼

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

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

局部變量在函數外部和其它函數不能使用

</>復制代碼

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

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

</>復制代碼

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

執行之后發現在外面使用的是全局變量的值 5555,在函數內部使用的局部變量的值 6666。

有了上面的這些基礎知識點,你應該注意下變量的使用范圍這個概念了,在面向對象的時候還將繼續學習。

9.7 傳遞任意數量的參數

9.7.1 一般參數與任意參數的組合

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

</>復制代碼

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

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

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

</>復制代碼

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

希望你現在發現規律,函數調用的時候先依次去匹配函數定義的參數,一一對應,都對應完畢,發現沒有一般參數了,剩下的都打包給 *keys 就好了。

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

9.7.2 一般參數與任意數量的關鍵詞參數

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

</>復制代碼

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

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

如果與一般參數進行匹配,規則也非常簡單。

</>復制代碼

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

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

9.8 函數以后的擴展

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

延期學習的知識點如下:

  • 遞歸函數
  • 匿名函數
  • 高階函數

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

</>復制代碼

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

9.9 這篇博客的總結

函數,初學階段,這是啥,為什么,不懂唉;學習中期,寫函數好舒服啊,面向對象那里有函數好用;學習后期,快點實現這個功能,啥方法都行,抓緊點,馬上下班了。

沒錯,函數就處在一個說難很難,轉身又很簡單的一個環節。

最后一碗毒雞湯

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

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


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