從 JavaScript 角度學 Python(5) - 函式

前言

前一篇聊了關於型別與變數的部分,所以這一篇當然就閃不了要聊聊關於函式,函式對於許多新手來講是一個很痛的痛點,所以這一篇我也會分享一些什麼時候該用函式的簡單概念與如何練習函式。

生活化函式

接著來聊聊函式的部分,雖然這一篇是想要從 JavaScript 角度學 Python,但是我之前寫過一篇文章叫做「有點長的淺談 JavaScript function 函式」,那麼為了避免過度深入探討 JavaScript 的部份因此如果有興趣的人可以考慮閱讀上述那一篇,而後面就會盡量著重於 Python 的部分哩。

那麼滿多人對於函式的概念沒有那麼清楚也不太清楚什麼時候要使用函式,所以我這邊簡單的描述與舉例情境函式使用的時機,也就是…

「重複性行為非常高的時候,你就可以包裝成函式。」

沒錯,就是這麼 Easy!這就是函式一開始在學習精髓。

哩洗勒工三小

好啦,我知道這時候你應該會說什麼叫重複性高的行為?我舉例一個簡單情境來講好了。

假設我們一週有五天,而第一天到第五天,絕對會有以下這些相同的生活行為:

  • 起床
  • 刷牙
  • 運動
  • 中午吃飯
  • 午睡
  • 下午運動
  • 晚上吃飯
  • 晚上洗澡
  • 睡覺

那麼如果將上面行為轉換成五天的程式碼就會類似這樣:

(下面先以我們熟悉的 JavaScript 角度撰寫一次)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
console.log('第一天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
console.log('第二天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
console.log('第三天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
console.log('第四天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
console.log('第五天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');

你可能會想說:「哈哈,我先打好一次複製貼上五次就好了,我還傻傻打五次?」

這確實也一種解法沒錯,但是相對程式碼會很攏長且不好維護,你自己也可以試著設想看看如果你未來哪一天突然開始更改自己的生活習慣呢?是不是就變成五天都要修改了呢?

因此如果善加利用函式來包裝的話,你就可以將重複性的行為減少撰寫,而著重於函式呼叫即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function fn() {
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
}
console.log('第一天');
fn();
console.log('第二天');
fn();
console.log('第三天');
fn();
console.log('第四天');
fn()
console.log('第五天');
fn();

上面的範例中你可以發現我只需要透過呼叫 fn() 就可以直接達到相同的行為,除此之外如果我跟你說今天這個重複行為要從 1~100 天呢?甚至是 365 天?那你不就要複製貼上到死掉了?而且善加利用函式將重複性的行為包裝起來還有另一個好處,當你要維護修改時,就可以統一全部修改,假設其中某一天我更改了我的生活模式 console.log('午睡'); 變成了 console.log('讀書');,那麼你就可以不用取代到死。

函式好棒棒!

當然上面這一段程式碼我們也可以加上迴圈的方式連 第一天第二天 等等都給包裝起來,這樣子做的話你想要跑幾天都沒有問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function fn(day) {
console.log('第'+ day +'天');
console.log('起床');
console.log('刷牙');
console.log('運動');
console.log('中午吃飯');
console.log('午睡');
console.log('下午運動');
console.log('晚上吃飯');
console.log('晚上洗澡');
console.log('睡覺');
console.log('---------');
}

var day = 5;

for(var i = 0; i < day; i+=1) {
fn(i + 1);
}

上面這些程式碼的結果我就不呈現了,如果對於函式的使用時機還是沒有那麼清楚的話,你也可以試著將自己生活上的重複性行為列出來,然後再試著轉換成程式碼,試著思考一下怎麼包裝成函式。

Python 的 def 定義

那麼讓我們回到 Python 的部分,在前面我們知道 JavaScript 宣告一個函式是使用 function 這個關鍵字,而 Python 呢?Python 則是使用 def,而 def 中文是「定義」某個東西的意思,所以讓我們直接將上方的 JavaScript 函式改寫成 Python 的試試看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fn(day):
print('第'+ day +'天')
print('起床')
print('刷牙')
print('運動')
print('中午吃飯')
print('午睡')
print('下午運動')
print('晚上吃飯')
print('晚上洗澡')
print('睡覺')
print('---------')

fn(1)

在宣告 def 的時候,請務必注意 def 名稱的後方要記得補上一個「:」號,否則是會出現錯誤的,最主要原因是因為 Python 的花括號 {} 只會在字典的時候用到(後面章節會在說明什麼是字典,這邊先知道這是什麼就好)。

另外上面這一段程式碼基本上是會出現錯誤的,而錯誤訊息主要是 TypeError: can only concatenate str (not "int") to str,這是什麼意思呢?簡單來講就是我們即將做字串組合的那一段程式碼所傳入的型別是錯誤的 (print('第'+ day +'天')),此時此刻的你應該會是你表情包的第一個表情:

WT...

畢竟我們在寫 JavaScript 字串組合的時候,通常都是這樣子組合 console.log('第'+ day +'天'); 字串的(當然還有樣板字面值啦)。

但是在 Python 上這樣做是會發生型別錯誤的,它這一段錯誤訊息的意思簡單來講就是整數型別 (int) 無法與字串型別 (string) 相加,所以我們可以知道一件事情 JavaScript 在做字串組合的時候是會幫我們隱含轉型的:

1
2
var a = 1;
console.log('第'+ a +'天');

意想不到吧?我也想不到,我寫一篇範例的時候才知道的。

因此在 Python 中會出現 TypeError: can only concatenate str (not "int") to str 的錯誤原因最主要是因為我們嘗試將字串型別 (string) 與整數型別 (int) 相加 (print('第'+ day +'天')) 所導致,正常來講字串只能跟字串相加,因此這一段如果要解決的話,你就必須先做型別轉換:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fn(day):
print('第'+ str(day) +'天')
print('起床')
print('刷牙')
print('運動')
print('中午吃飯')
print('午睡')
print('下午運動')
print('晚上吃飯')
print('晚上洗澡')
print('睡覺')
print('---------')

fn(1)

至於迴圈該怎麼寫,這邊先不介紹,我們保留到下一次再介紹(否則這一篇會太長)。

除此之外這邊要注意 Python 的型別轉換都是使用全小寫,底下也列給各位看一下:

  • str()
  • int()
  • bool()
  • float()

與 JavaScript 型別轉換首字大寫是不同的:

  • String()
  • Number()
  • Boolean()

Python 與 JavaScript 的函式差異

接下來聊聊 Python 的函式與 JavaScript 函式有哪些差異之處,JavaScript 的函式有幾個常見的特性:

  • 可以使用變數儲存函式 (匿名表達式)
  • 可以在函式陳述式建立之前呼叫 (Hoisting 與 具名函式陳述式)
  • IIFE (立即執行函式)

下面我先舉例一些常見的 JavaScript 函式陳述式、函式表達式與 IIFE 的撰寫方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 第一種
fn1();
function fn1() {
console.log('Hello Ray');
}

// 第二種
const fn2 = function() {
console.log('Hello Ray');
}
fn2();

// 第三種
(function() {
console.log('Hello Ray');
})();

首先先講講第一種函式宣告的方式也就是在函式建立之前呼叫函式,基本上 JavaScript 是允許你在函式建立之前呼叫函式(通常不建議這樣做),那…

Python 呢?Python 一樣也可以這麼做嗎?答案是不行的:

1
2
3
4
fn1()

def fn1():
print('Hello Ray')

上面這一段 Python 程式碼是會出現 NameError: name 'fn1' is not defined 的錯誤訊息,因此在一次驗證 JavaScript 的提升 (Hoisting) 美好(誤)

接下來要直接跳到第二種匿名表達式與 IIFE 的寫法,但在 Python 會比較特別,所以讓我們直接跳到下一個章節。

lambda

接下來是關於直接將函式儲存到變數內,這邊在 Python 中如果要做到這個行為就必須使用一個運算子叫做 lambda,而 lambda 就是所謂的匿名函式,它的特性顧名思義也就是:

  • 不用宣告名稱
  • 但只能有一行程式碼,若換行則會出錯

所以 lambda 該怎麼用呢?讓我們直接來看看範例程式碼:

1
2
myName = lambda name: print('Hello ' + name)
myName('Ray') # Hello Ray

是不是覺得短的不像話?喜好簡潔的你感受到快樂了呢?

喜好簡潔的男子

所以這邊先聊簡單聊一下 Lambda 函式,由於 Lambda 函式只能撰寫一行(意指你換行就會噴錯),所以基本上還是會有一些小限制,可是卻非常精簡且強大,而 Lambda 包含了三個重點部分:

  • Lambda - 關鍵字
  • 參數列表 - parameter
  • 表達式 - expression
1
lambda parameter1, parameter2,...: expression

(如果你對於表達式與陳述式不熟悉的話,可以詳見 此篇 文章。)

假設你硬是把 lambda 改成多行的話,是會出現錯誤的:

1
2
3
# SyntaxError: invalid syntax
myName = lambda name:
print('Hello ' + name)

那麼 JavaScript 中的 IIFE 呢?第三個 IIFE (立即執行函式)也會與 Lambda 有關係,廢話就不多說了,直接來看範例程式碼:

1
(lambda name: print('Hello ' + name))('Ray') # Hello Ray

看到現在是不是有一種與 JavaScript 的 IIFE 似曾相見的感覺呢?

這是 JavaScript 嗎?

最後想提醒一下,雖然 lambda 很強大很好用,但是卻也要小心使用,因為它非常簡潔的關係且強制只能寫一行,那麼在程式碼可讀性上可能會不好閱讀唷。

參考文獻

作者的話

因為買了一大罐花雕酒,所以這陣子一直在瘋狂做花雕醉雞(雞肉我是買好市多的清雞肉),嘗試了普通版本的花雕醉雞,第一次試做整個大失敗,有點太鹹花雕酒加太多。

關於兔兔們

兔法無邊

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ