Python中的函數(shù)缠沈、作用域、封裝 | 寫(xiě)給小白的工程師入門(mén)

題圖來(lái)自 Pexels

這是我面向小白寫(xiě)的Python編程教程的第七篇错蝴。拿勺子同學(xué)當(dāng)小白鼠講過(guò)一遍后洲愤,就把修改完的講義發(fā)出來(lái)啦。

如果你認(rèn)可這篇教程的價(jià)值顷锰,歡迎分享到朋友圈禽篱,分享給更多人!有看不懂的地方也可以留言或者問(wèn)詢(xún)馍惹。越多關(guān)注,作者就越多動(dòng)力及時(shí)更新吶 ??

.

"Functions should do one thing. They should do it well. They should do it only."

— Robert C. Martin

這節(jié)講講 Python 中的函數(shù)玛界,如何自定義函數(shù)万矾,如何理解變量作用域,最后捎帶粗講一下面向?qū)ο缶幊讨械?strong>封裝概念慎框。

.

A. 函數(shù)定義與基本用法

函數(shù)(function)是一組復(fù)合語(yǔ)句良狈,可以接收輸入值,執(zhí)行特定命令笨枯,選擇性地返回輸出結(jié)果薪丁。

Python 中函數(shù)與數(shù)學(xué)里的函數(shù)相似。比如這個(gè)代數(shù)函數(shù):

f(x) = x * 2

等式左邊定義了一個(gè)函數(shù) f 馅精,這個(gè)函數(shù)需要一個(gè)輸入值严嗜、自變量 x ——即為參數(shù)(parameter)。一個(gè)函數(shù)可以帶一個(gè)或多個(gè)參數(shù)洲敢,也可以不用參數(shù)漫玄。

等式右邊就是這個(gè)函數(shù)的具體定義了。這個(gè)函數(shù)可以把傳入的參數(shù)乘二,并返回結(jié)果睦优。

.

在代數(shù)和 Python 中渗常,呼出(= “調(diào)用”、使用)函數(shù)的語(yǔ)法都是一樣的:

FUNCTION_NAME(PARAMETERS_SEPERATED_BY_COMMA)

(在這套教程里汗盘,我用約定俗成的「大寫(xiě)字母 + 下劃線(xiàn)」來(lái)代表應(yīng)該用真實(shí)代碼取代的內(nèi)容皱碘;在編程中這樣的習(xí)慣用法很多。)

比如我們可以傳入?yún)?shù) 4到上面的代數(shù)方程中隐孽,

f(4)

>> 8

再比如癌椿,print() 是一個(gè)我們已經(jīng)很熟悉的 Python 3 內(nèi)建函數(shù) (built-in function) 了:

>>> print("Whatever you like :p")
Whatever you like :p

.

自定義函數(shù)

由程序員來(lái)定義一個(gè)函數(shù)在 Python 中極度常見(jiàn)。比如缓醋,上面的代數(shù)方程用 Python 自定義函數(shù)表達(dá)即為:

def f(x):
    return x * 2

這幾乎是最簡(jiǎn)形式的函數(shù)定義了如失,由此我們不難推斷出 Python 自定義函數(shù)的基本規(guī)則:

def FUNCTION_NAME(PARAMETERS_SEPERATED_BY_COMMA):
    FUNCTION_DEFINITION

定義函數(shù)的關(guān)鍵字為 def,函數(shù)名稱(chēng)可以隨意取送粱,但應(yīng)該避免 Python 關(guān)鍵字(keywords)褪贵;習(xí)慣上 Python 函數(shù)命名通常為「小寫(xiě)字母 + 下劃線(xiàn)」組合,例如 print_area, multiply, even_or_odd.

函數(shù)名稱(chēng)后緊跟一對(duì)單括號(hào) ()抗俄,如果有參數(shù)的話(huà)應(yīng)該放在括號(hào)里脆丁,多個(gè)參數(shù)以逗號(hào)隔開(kāi)。不要忘記句末冒號(hào)动雹。

# 零參數(shù)的函數(shù)
>>> def hello():
...     return 'Hello Python!'
...
>>> greeting = hello()
>>> print(greeting)
Hello Python!

# 多個(gè)參數(shù)的函數(shù)
>>> def sum_all(x, y, z):
...     return x + y + z
...
>>> my_sum = sum_all(1,2,3)
>>> print(my_sum)
6

.

所有跟在 def 行后面縮進(jìn)的語(yǔ)句都是該函數(shù)的定義槽卫。上面 f(x) 的函數(shù)定義只有一行,且用到了 return 關(guān)鍵字胰蝠。return 后跟著的表達(dá)式或值即為這個(gè)函數(shù)會(huì)返回的輸出值歼培。函數(shù)定義可以不包括 return 語(yǔ)句,沒(méi)有 return 的函數(shù)返回值為 None. 比如:

# calling a function with return statement
>>> f(2)
4
>>> result = f(2)
>>> print(result)
4

# create a function without return statement
>>> def no_return(x):
...     x = x * 2
...     print('multiplied by 2:', x)
...
# call a function without return statement
>>> no_return(4)
multiplied by 2: 8

注意:很容易忽略的一點(diǎn)是茸塞,當(dāng)你想要保存一個(gè)函數(shù)的輸出值(以便之后使用)時(shí)躲庄,需要新建一個(gè)變量(variable),用這個(gè)變量來(lái)保存函數(shù)的輸出結(jié)果钾虐,比如上面的 result 變量噪窘。

.

函數(shù)的復(fù)用

為什么要?jiǎng)?chuàng)建、調(diào)用函數(shù)而不是直接寫(xiě)出具體指令呢效扫?

因?yàn)橛幸粋€(gè)現(xiàn)成函數(shù)可以大大降低程序員的碼字工作量倔监。想實(shí)現(xiàn)什么功能第一反應(yīng)應(yīng)該是,“是否有現(xiàn)成的 函數(shù)/方法/模塊 可用菌仁?“——而不是重復(fù)造輪子 (reinventing the wheel) 浩习。

To reinvent the wheel is to duplicate a basic method that has already previously been created or optimized by others.

— Wikipedia

當(dāng)前人已經(jīng)發(fā)明、優(yōu)化出一種解決問(wèn)題的方法后掘托,后人若還要自己從零開(kāi)始新造自己的方法瘦锹,這就被稱(chēng)為「重復(fù)造輪子」。在大部分情況下,應(yīng)該盡量避免「重復(fù)造輪子」弯院。

.

調(diào)用已經(jīng)寫(xiě)好的函數(shù)辱士,就是一種減少「自造輪子」,提高代碼復(fù)用性(reusability)的方式听绳。

例如颂碘,我們可以用這節(jié)課學(xué)到的函數(shù)自定義方法和上節(jié)課條件控制語(yǔ)句寫(xiě)一個(gè)“判斷輸入值奇偶性”的函數(shù):

def even_odd(x):
    if x % 2 == 0:
        print('input is even')
    else:
        print('input is odd')

接著調(diào)用這個(gè)函數(shù):

>>> even_odd(2)
input is even
>>> even_odd(3)
input is odd

接下來(lái)每次需要用到這個(gè)功能(functionality)的時(shí)候,只要一行代碼調(diào)用 even_odd 函數(shù)即可椅挣,大大減少了重復(fù)的工作量头岔。

.

嵌套函數(shù)

上一節(jié),我們認(rèn)識(shí)了嵌套條件語(yǔ)句(nested conditionals)鼠证,就是在一個(gè)條件語(yǔ)句的 True 分支后又接了一個(gè)條件句峡竣。在函數(shù)定義中,嵌套函數(shù)(nested functions)也是允許的量九。

例如适掰,

def outer():
    print('Outer Funtion!')
    
    def inner():
        print('Inner Function!')
    inner()

外層函數(shù)(outer function)與內(nèi)層函數(shù)(inner function)的命名也不言自明。

此時(shí)調(diào)用 outer 函數(shù)荠列,會(huì)是出現(xiàn)什么結(jié)果呢类浪?

>>> outer()
Outer Funtion!
Inner Function!

.

內(nèi)建函數(shù) Built-in functions

如果我們需要用的所有函數(shù)都要自己一一寫(xiě),那寫(xiě)代碼效率就太太低了肌似。因此费就,Python 是有自帶內(nèi)建函數(shù)庫(kù)的。

比如川队,剛剛提到的 print() 函數(shù)力细。常見(jiàn)的 Python 內(nèi)建函數(shù)還有以下這些:

# len() returns the length of an object
>>> len('Monty')
5
>>> len('12345')
5

# type() returns the data type of an object
>>> type('Welcome to SinanTalk!')
<class 'str'>
>>> type(98)
<class 'int'>

# str() takes an onject and returns a new object with a string data type
>>> str(100)
'100'

# int() takes an object and returns a new object with an integer data type
>>> int('99')
99

# float() takes an object and returns a new object with a floating point data type
>>> float(88)
88.0

# input() collects information from the user
>>> age = input('How old are you? ')
How old are you? 18
>>> age = int(age)
>>> if age < 28:
...     print('Still young!')
... else:
...     print('You must have seen a lot!')
...
Still young!
>>>

.

.

B. 變量作用域 Scope

是時(shí)候了解變量的作用域這個(gè)重要概念了!

作用域(scope)是變量(variable)的重要特性之一固额。

變量作用域決定了哪一部分程序可以訪(fǎng)問(wèn)某個(gè)特定的變量艳汽,即為對(duì)一個(gè)變量的「訪(fǎng)問(wèn)權(quán)限」。

A variable's scope: refers to what part of your program has access to the variable.

變量的訪(fǎng)問(wèn)權(quán)限是有這個(gè)變量的賦值位置決定的对雪。

為什么一開(kāi)始講變量的時(shí)候沒(méi)有涉及這個(gè)重要概念呢?

因?yàn)樵跊](méi)學(xué)自定義函數(shù)之前米绕,是很難理解變量作用域是怎么回事瑟捣。

.

根據(jù)作用域,變量可分為兩大類(lèi):

全局變量(global variable):定義/賦值在函數(shù)(或類(lèi))之外的變量栅干;

局部變量(local variable):定義/賦值在函數(shù)(或類(lèi))內(nèi)的變量迈套。

對(duì)于一個(gè)全局變量而言,在這個(gè)程序的任何位置都可以訪(fǎng)問(wèn)它碱鳞;而局部變量則只能在局部(即某個(gè)函數(shù)/類(lèi)的內(nèi)部)訪(fǎng)問(wèn)桑李,走出了這個(gè)函數(shù)/類(lèi),就不能再訪(fǎng)問(wèn)局部變量了。

.

打個(gè)比方贵白,在你面前站著一面放滿(mǎn)書(shū)的書(shū)柜率拒,你可以看到每本書(shū)的書(shū)名,但只有選其中一本書(shū)翻開(kāi)禁荒,才能看到這本書(shū)里提到的人名猬膨。

這面書(shū)柜就是一個(gè)程序,每本書(shū)都是一個(gè)代碼塊呛伴,寫(xiě)在書(shū)脊上的書(shū)名是全局變量勃痴,需要翻開(kāi)某本書(shū)才能看到的人名是局部變量——站在書(shū)架前是看不見(jiàn)局部變量的!

.

我們來(lái)看看在具體代碼中的全局/局部變量热康。

>>> x = 1
>>> y = 2
>>> z = 3
>>> print(x, y, z)
1 2 3

全局變量可以從程序的任何位置訪(fǎng)問(wèn)沛申,也包括函數(shù)內(nèi)部:

>>> def print_vals():
...     print(x, y, z)
...
>>> print_vals()
1 2 3

.

如果在函數(shù)內(nèi)部定義了局部變量,則不能在函數(shù)外單獨(dú)訪(fǎng)問(wèn)姐军,否則會(huì)拋出 NameError 異常:

>>> def print_new_vals():
...     val = 100
...     print(val)
...
>>> val
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'val' is not defined

這種情況下铁材,Python 解釋器看不見(jiàn)局部變量 val ,有點(diǎn)像選擇性失明庶弃。

.

如果在函數(shù)內(nèi)容定義一個(gè)與已經(jīng)存在的全局變量名稱(chēng)相同的局部變量衫贬,那么在這個(gè)函數(shù)外再訪(fǎng)問(wèn)此變量,會(huì)返回什么值呢歇攻?

>>> x = 1
>>> def local_val():
...     x = 100
...
>>> x

在 IDLE 內(nèi)敲一遍的話(huà)會(huì)發(fā)現(xiàn)固惯,x 返回的值是 1。這個(gè)例子更加充分地證明了局部變量和全局變量的作用域差別缴守。

.

如果想在函數(shù)內(nèi)部改變某個(gè)全局變量的話(huà)葬毫,可以用 global 這個(gè)關(guān)鍵字來(lái)表明這兒訪(fǎng)問(wèn)、修改的是全局變量屡穗。

>>> x = 1
>>> def f():
...     global x
...     x += 1
...     print(x)
...
>>> f()
2

.

為什么編程語(yǔ)言中的變量普遍需要規(guī)定作用域呢贴捡?

如果不存在作用域限制的話(huà),一個(gè)程序中的任何變量在任何位置都可以訪(fǎng)問(wèn)村砂。那么在一個(gè)很長(zhǎng)的程序中烂斋,如果你在一個(gè)函數(shù)內(nèi)部使用了全局變量且不小心改變了這個(gè)變量的值或類(lèi)型,那在接下來(lái)的程序中這個(gè)變量就可能會(huì)擁有不同的特性础废,引發(fā)意想不到的錯(cuò)誤汛骂。

如果有多個(gè)程序員碰過(guò)同一個(gè)程序,可能并不是每個(gè)人都清楚別人命名的變量评腺,沒(méi)有作用域的訪(fǎng)問(wèn)限制的話(huà)帘瞭,很可能會(huì)出錯(cuò)。

.

變量的作用域不同同時(shí)也引出了編程中封裝(encapsulation)的概念蒿讥。

.

.

C. 函數(shù)的封裝

Encapsulation

封裝是面向?qū)ο缶幊蹋╫bject-oriented programming)中的重要概念蝶念。今天我們只粗淺地介紹一點(diǎn)和函數(shù)有關(guān)的封裝行為抛腕。

封裝,顧名思義媒殉,就像把一些代碼封起來(lái)裝進(jìn)膠囊(capsule 在英文中可表示“膠囊”)或瓶子里去担敌,在膠囊外的代碼不能訪(fǎng)問(wèn)膠囊內(nèi)代碼。

When code is encapsulated, it means when it is called, the caller cannot access the code's internal data.

比如适袜,上面介紹的函數(shù)內(nèi)部的局部變量柄错,就是函數(shù)封裝行為的體現(xiàn)。

這樣做的好處很多苦酱。當(dāng)用戶(hù)(指所有使用這個(gè)程序的人)運(yùn)行你的代碼時(shí)售貌,他并不需要知道你的代碼里有什么函數(shù),每個(gè)函數(shù)內(nèi)部又存在怎樣的變量和運(yùn)算疫萤,他只需要知道如何運(yùn)行即可颂跨。另一方面,正常情況下扯饶,一個(gè)程序會(huì)需要反復(fù)優(yōu)化升級(jí)恒削,后期你可能會(huì)修改一個(gè)函數(shù)內(nèi)部的代碼,沒(méi)有封裝的話(huà)尾序,這個(gè)函數(shù)外部的代碼可能會(huì)直接訪(fǎng)問(wèn)內(nèi)部钓丰,一旦修改內(nèi)部代碼,就可能會(huì)造成“連鎖事故”每币。

所以說(shuō)携丁,封裝是提高代碼可維護(hù)性與可移植性的重要前提。

.

第7節(jié) 小結(jié)

掌握本節(jié)的自定義函數(shù)與上一節(jié)的條件語(yǔ)句兰怠,已經(jīng)能寫(xiě)很多 Python 短程序了呢梦鉴!
.
.

謝謝你的閱讀

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市揭保,隨后出現(xiàn)的幾起案子肥橙,更是在濱河造成了極大的恐慌,老刑警劉巖秸侣,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件存筏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡味榛,警方通過(guò)查閱死者的電腦和手機(jī)方篮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)励负,“玉大人,你說(shuō)我怎么就攤上這事匕得〖逃埽” “怎么了巾表?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)略吨。 經(jīng)常有香客問(wèn)我集币,道長(zhǎng),這世上最難降的妖魔是什么翠忠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任鞠苟,我火速辦了婚禮,結(jié)果婚禮上秽之,老公的妹妹穿的比我還像新娘当娱。我一直安慰自己,他們只是感情好考榨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布跨细。 她就那樣靜靜地躺著,像睡著了一般河质。 火紅的嫁衣襯著肌膚如雪冀惭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天掀鹅,我揣著相機(jī)與錄音散休,去河邊找鬼。 笑死乐尊,一個(gè)胖子當(dāng)著我的面吹牛戚丸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播科吭,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼昏滴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了对人?” 一聲冷哼從身側(cè)響起谣殊,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牺弄,沒(méi)想到半個(gè)月后姻几,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡势告,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蛇捌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咱台。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡络拌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出回溺,到底是詐尸還是另有隱情春贸,我是刑警寧澤混萝,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站萍恕,受9級(jí)特大地震影響逸嘀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜允粤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一崭倘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧类垫,春花似錦司光、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至购撼,卻和暖如春跪削,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迂求。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工碾盐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揩局。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓毫玖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親凌盯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子付枫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容