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

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

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

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

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

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

9.2 函數(shù)初接觸

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

9.2.1 函數(shù)的定義

具體的語(yǔ)法格式如下:

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

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

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

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

返回值,非必須,返回多個(gè)值使用逗號(hào) , 分隔即可。

注意第一行末尾的分號(hào)

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

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

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

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

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

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

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

9.3 函數(shù)的參數(shù)設(shè)計(jì)

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

9.3.1 傳遞一個(gè)參數(shù)

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

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

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

9.3.2 傳遞多個(gè)參數(shù)

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

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

多個(gè)參數(shù)一定要注意參數(shù)的位置,前后順序如果錯(cuò)誤,會(huì)導(dǎo)致嚴(yán)重 BUG。上述代碼中 橡皮擦 會(huì)傳遞給 name,20 會(huì)傳遞給 age。

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

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

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

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

9.3.4 參數(shù)默認(rèn)值

在定義函數(shù)的時(shí)候可以給參數(shù)一個(gè)默認(rèn)值,如果調(diào)用該函數(shù)沒(méi)有給該參數(shù)賦值,程序會(huì)使用默認(rèn)值而不會(huì)報(bào)錯(cuò)。

# 沒(méi)有默認(rèn)值的參數(shù)def show(name):    print("傳遞進(jìn)來(lái)的姓名是:", name)show() # 該函數(shù)調(diào)用時(shí)會(huì)報(bào)錯(cuò)# 有默認(rèn)值的參數(shù)def show(name="橡皮擦"):    print("傳遞進(jìn)來(lái)的姓名是:", name)show() # 該函數(shù)調(diào)用沒(méi)有問(wèn)題,即使沒(méi)有傳遞參數(shù)會(huì)使用默認(rèn)值

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

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

9.4 函數(shù)返回值

函授的返回值屬于非必選項(xiàng),可以寫可以不寫,不寫的時(shí)候也會(huì)有返回值,該值為 None。

9.4.1 返回 None

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

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

得到的 ret 的值是 None,表示沒(méi)有返回值。

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

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

9.4.2 返回一個(gè)值

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

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

9.4.3 返回多個(gè)值

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

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

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

9.4.4 返回字典與列表

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

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

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

為什么多帶帶將其參數(shù)是列表時(shí)拿出來(lái)講解,是因?yàn)榱斜磉@個(gè)有點(diǎn)特殊,里面還會(huì)引出全局變量與局部變量的一個(gè)概念,放心第一遍學(xué)習(xí) 100%迷糊。如果你有其他編程語(yǔ)言的功底,那另當(dāng)別論。

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

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

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

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

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

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

9.6 局部變量與全局變量

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

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

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

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

score = 1def change_list():    # 輸出全局變量 score,因?yàn)檫@個(gè)變量在函數(shù)外面聲明的,大家都可以用    print(score)change_list()# 在函數(shù)外面也可以使用print(score)

上面的 score 并未在函數(shù)內(nèi)部聲明,但是函數(shù)內(nèi)部也可以訪問(wèn)到,那函數(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)

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

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

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

有了上面的這些基礎(chǔ)知識(shí)點(diǎn),你應(yīng)該注意下變量的使用范圍這個(gè)概念了,在面向?qū)ο蟮臅r(shí)候還將繼續(xù)學(xué)習(xí)。

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

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

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

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ù)名,然后在函數(shù)體重的代碼塊位置就可以進(jìn)行循環(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ù)定義的參數(shù),一一對(duì)應(yīng),都對(duì)應(yīng)完畢,發(fā)現(xiàn)沒(méi)有一般參數(shù)了,剩下的都打包給 *keys 就好了。

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

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

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

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

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

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

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

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

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

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

延期學(xué)習(xí)的知識(shí)點(diǎn)如下:

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

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

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

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

最后一碗毒雞湯

編程需要一個(gè)頓悟的時(shí)刻,而這個(gè)時(shí)刻很多人就沒(méi)有過(guò)。 O(∩_∩)O 哈哈~

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


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