Python 學習的第一個難關 -- 函數,這個地方學會的人覺得沒有啥,沒學過的學的時候迷迷瞪瞪,不用慌,學編程就是這樣,先學過去,在從上帝視角去看,堅持打卡,我一天寫一篇,你可以兩天學一篇。
九、Python 中函數是基礎部分第一道難關
9.1 函數出現的背景
為什么在編程語言中會出現函數概念,目的有 2 個。
- 在開發的時候,很多項目都是團隊開發,把代碼功能編寫成一個個的函數,方便維護,每個人也可相對獨立的開發,縮短整體開發時間
- 代碼編寫成函數以后,重復的功能只寫一次即可,其它地方可以直接調用,方便了代碼的復用。
直接寫出這兩個目的還是沒有直觀的感受,要在代碼中感受函數的用法。
9.2 函數初接觸
咱從本系列博客一開始就已經接觸函數的概念了,例如第一篇就學習的 print
, 就是一個內置函數,相似的還有 len
、add
、sorted
這些,調用函數時最大的便捷性就是不用知道函數內部的具體實現,就可以用該函數使用自己想要的效果。
9.2.1 函數的定義
具體的語法格式如下:
</>復制代碼
def 函數名稱(參數1[,參數2,參數3...]): 代碼塊 代碼塊 return 返回值
跟 if
語句,for
語句一樣,函數定義與使用的時候要注意下代碼縮進。
函數名稱必須是唯一的,進行聲明的有意義,不要使用 a
當做函數名,以后很有可能就忘記了這個函數是做什么用的。
參數值,非必須,根據函數的要求自行設定即可,各個參數值之間用 ,
分隔。
返回值,非必須,返回多個值使用逗號 ,
分隔即可。
注意第一行末尾的分號
9.2.2 無參數無返回值的函數
該內容將演示函數的使用便捷性。
</>復制代碼
# 創建一個函數def show(): print("我是一個無參數無返回值的函數") print("hello world")show()show()show()show()show()
函數聲明之后,通過 函數名()
進行調用,上面的代碼寫了 5 次 show()
,表示函數被調用了 5 次。
如果不通過函數實現上文效果,需要將函數內部的代碼復制 5 次。
</>復制代碼
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 傳遞一個參數
函數內有參數,代碼如下:
</>復制代碼
# 聲明一個帶一個參數的函數def show(name): print("傳遞進來的姓名是:", name)show("橡皮擦")show("大橡皮擦")show("小橡皮擦")
小括號里面的 name
即為參數,該參數能在函數內部代碼塊直接使用。
9.3.2 傳遞多個參數
多個參數只需要在函數聲明時小括號里面多增加幾個參數即可。
</>復制代碼
# 聲明一個帶多參數的函數def show(name, age): print("傳遞進來的姓名是:", name, " 年齡是:", age)show("橡皮擦", 20)show("大橡皮擦", 21)show("小橡皮擦", 18)
多個參數一定要注意參數的位置,前后順序如果錯誤,會導致嚴重 BUG。上述代碼中 橡皮擦
會傳遞給 name
,20
會傳遞給 age
。
9.3.3 關鍵詞參數(參數名稱=值)
該參數使用的方式是在調用函數時,參數用 參數名稱=值
這種形式傳遞。其實在上述傳遞一個和多個參數的時候,也可以應用該方式傳遞參數,例如修改代碼如下。
</>復制代碼
# 聲明一個帶一個參數的函數def show(name): print("傳遞進來的姓名是:", name)show(name="橡皮擦")# 聲明一個帶多參數的函數def show1(name, age): print("傳遞進來的姓名是:", name, " 年齡是:", age)show1(name="橡皮擦", age=20)
用這種形式調用函數,參數的位置就不在重要了,因為已經指定參數具體值是多少。
9.3.4 參數默認值
在定義函數的時候可以給參數一個默認值,如果調用該函數沒有給該參數賦值,程序會使用默認值而不會報錯。
</>復制代碼
# 沒有默認值的參數def show(name): print("傳遞進來的姓名是:", name)show() # 該函數調用時會報錯# 有默認值的參數def show(name="橡皮擦"): print("傳遞進來的姓名是:", name)show() # 該函數調用沒有問題,即使沒有傳遞參數會使用默認值
如果一個參數有默認值,注意該參數必須放在函數參數位置的最右側,例如下述函數定義。
</>復制代碼
def show(a,b,c,name="橡皮擦"): pass
9.4 函數返回值
函授的返回值屬于非必選項,可以寫可以不寫,不寫的時候也會有返回值,該值為 None。
9.4.1 返回 None
函數如果沒有寫返回值即 return
,那 Python 會自動在函數體內增加一行代碼 return None
。函數的返回值可以賦值給一個變量,通過打印該變量,即可知道返回的具體內容。
</>復制代碼
# 沒有返回值的函數def show(): print("注意下面沒有 return")ret = show()print(ret)
得到的 ret
的值是 None,表示沒有返回值。
如果只寫 return,也會返回 None,注意下述代碼是正確的。
</>復制代碼
# 沒有返回值的函數def show(): print("注意下面沒有 return") returnret = show()print(ret)
9.4.2 返回一個值
函數常見的情況是有返回值的,例如執行一段計算代碼之后返回計算結果。
</>復制代碼
# 定義一個減法函數def sub(a, b): c = a - b return c# 參數為 2 和 1,將結果進行返回ret = sub(2, 1)print(ret)
9.4.3 返回多個值
使用 return 返回函數數據,可以一次性返回多個值,返回的數據之間用逗號分隔即可。
</>復制代碼
# 定義一個減法函數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 返回字典與列表
函數可以返回字符串類型的變量,這個與返回一個值一樣,只是具體數據類型的區別,除了上述提及的內容,函數也可以返回比較復雜的數據,例如字典或者列表,該內容沒有特別說明的,你只需要將字典和列表看成普通的數據類型進行返回即可,例如下述代碼。
</>復制代碼
def sub1(): return [1, 2, 3]def sub2(): return {"name": "橡皮擦", "loc": "CSDN"}
9.5 調用函數時參數是列表
為什么多帶帶將其參數是列表時拿出來講解,是因為列表這個有點特殊,里面還會引出全局變量與局部變量的一個概念,放心第一遍學習 100%迷糊。如果你有其他編程語言的功底,那另當別論。
具體代碼如下,注意看出現的問題。
</>復制代碼
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
被函數給修改了。疑問是所有在函數外面的變量傳遞到參數內部都會被修改嗎?換個整數試試。
</>復制代碼
score = 1def change_list(one_score): one_score = 333# 函數調用,同時將 score 作為參數傳遞進函數內部change_list(score)print(score)
此時 score
雖然在函數內部被修改為了 333,但是在函數外打印并沒有被修改,還是 1?,F在問題就出現了,Python 并不是一視同仁的,列表變量在函數內被修改影響到了外部變量,而整型變量并沒有受到影響。
這個坑咱先挖下,以后慢慢填,為啥列表在函數內修改,函數外面也會受到影響,涉及到內存地址空間這些更底層的概念了。先學習一下局部變量與全局變量。
9.6 局部變量與全局變量
這個概念非常難理解,是的,如果你理解了在這個階段也是一知半解。
在設計函數的時候,有時需要控制變量的使用范圍,如果變量的使用范圍在函數內部,那這個變量叫做局部變量,注意是函數內部。如果某個變量的使用范圍在整個程序,這個變量就是全局變量。
上述內容只是初次接觸,在函數部分咱先簡單弄個概念在心里在說。
全局變量在所有函數都能用
</>復制代碼
score = 1def change_list(): # 輸出全局變量 score,因為這個變量在函數外面聲明的,大家都可以用 print(score)change_list()# 在函數外面也可以使用print(score)
上面的 score
并未在函數內部聲明,但是函數內部也可以訪問到,那函數外面的變量 score
就是全局變量。
局部變量在函數外部和其它函數不能使用
</>復制代碼
def change_list(): # 局部變量 score,本函數可以用 score = 1 print(score)change_list()# 局部變量 score,在函數外面不可以使用print(score)# 其它函數內部也不可以使用def show(): print(score)
如果局部變量和全局變量名稱重復
在程序設計的時候很容易出現重名的情況,這種情況比較尷尬了,初學難度繼續增加。
</>復制代碼
score = 5555def change_list(): # 局部變量 score,本函數可以用 score = 6666 print(score)change_list()# 外面使用的全局變量 scoreprint(score)
執行之后發現在外面使用的是全局變量的值 5555,在函數內部使用的局部變量的值 6666。
有了上面的這些基礎知識點,你應該注意下變量的使用范圍這個概念了,在面向對象的時候還將繼續學習。
9.7 傳遞任意數量的參數
9.7.1 一般參數與任意參數的組合
在 Python 編寫代碼的過程中,很容易出現一種情況是你不知道有多少個參數,這時函數定義時參數就不好設定了,好在 Python 已經想到了這個情況。
</>復制代碼
def show(*keys): print("傳入的參數可以循環打印") for key in keys: print(key)show(1,2,3,4,5,6,7)
在函數定義的時候,參數位置使用 *參數名
,然后在函數體重的代碼塊位置就可以進行循環打印了,可以捕獲到任意數量的參數。
如果又有一般參數,又有不定數量的參數怎么辦?好辦,使用下面的格式。
</>復制代碼
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 一般參數與任意數量的關鍵詞參數
參數里面還有一種情況是需要傳不定數量的關鍵詞參數,這又如何設計呢?
</>復制代碼
def show(**args): print("傳入的參數可以循環打印") for key in args.items(): print(key)show(name="橡皮擦", age=18)
這種情況,你把傳遞進去的所有參數當成一個字典即可,自動聚合成了一個字典類型的數據。
如果與一般參數進行匹配,規則也非常簡單。
</>復制代碼
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)
在程序中是怎么解析的呢?順序變動可不可以呢?
</>復制代碼
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 天。
如果你有想要交流的想法、技術,歡迎在評論區留言。