從 JavaScript 角度學 Python(9) - 容器型別(上)

前言

BMI 作業做得還好嗎?不知道你有沒有完成,但是這邊時間不等人,所以我們就接著聊聊下一個主題吧。

什麼是容器型別?

接下要聊聊的是 Python 另一種型別,也就是容器型別。

其實在 JavaScript 中這個型別就是所謂的物件型別,只是在 Python 則是稱為容器型別。

那…什麼是容器型別呢?容器型別你可以把它想像成類似使用一個個的馬克杯或者是盒子,可以用來放各種東西,就如同前面所言以 JavaScript 來講的容器型別就是陣列與物件:

1
2
3
4
5
6
7
// 陣列 Array
var arr = ['Ray'];

// 物件 Object
var obj = {
name: 'Ray',
};

(這邊要注意陣列依然是屬於物件型別,原因可詳見:JavaScript 核心觀念(31)-物件-陣列)

那麼在 Python 上並不稱為陣列與物件,而是串列(List) 與字典 (Dictionaries):

  • Array(陣列) === List(串列)
  • Object(物件) === Dictionaries(字典)

那麼寫法會不會有差異呢?基本上串列是沒有什麼太大的差異,但是在字典上就會有一點差異:

1
2
3
4
5
6
7
# 串列
lis = ['Ray']

# 字典
dic = {
'name': 'Ray',
}

為什麼會說字典有一點差異呢?先讓我們轉過來看一下 JavaScript 的部分,在 JavaScript 的物件屬性我們是可以省略單/雙引號的:

1
2
3
var obj = {
name: 'Ray',
};

但是在 Python 是不能省略這個單/雙引號的,否則你是會出現 NameError: name 'name' is not defined 的唷~

NameError: name 'name' is not defined

那麼由於這個錯誤訊息的關係,代表著沒有使用單/雙引號包覆的這個屬性,是會被 Python 認為這是 一個變數

因此我們可以大膽的嘗試與挑戰一件事情,假使我宣告了一個變數並且字典的屬性不用使用單/雙引號包覆,其結果又會如何呢?

1
2
3
4
5
6
7
name = 'myName'

dic = {
name: 'Ray',
}

print(dic) # ??

我們可以看到結果是可以正常運作的:

執行成功

是不是發現與我們原本的 JavaScript 有些許不同呢?在一般的情況下 JavaScript 是不允許你將物件的屬性變成變數傳入的,不管怎樣都會被 JavaScript 轉換成一個字串屬性,只是在呈現與撰寫上它會忽略並允許你這樣做而已:

1
2
3
4
5
6
7
var name = 'myName';

var obj = {
name: 'Ray',
};

console.log(obj); // { name: "Ray" }

但是如果真的有這種例外狀況,也就是物件的屬性是必須透過變數所傳入的話,其實也可以做到,這時候你所必須使用是中括號運算子來達到這個需求:

1
2
3
4
5
6
7
var name = 'myName';

var obj = {
[name]: 'Ray',
};

console.log(obj); // { myName: "Ray" }

See!效果基本上是一樣的,只是變成另一種呈現方式罷了。

容器型別取值

接下來就是容器取值,串列取值與字典取值的部分會與原本的 JavaScript 也有差別嗎?先讓我們回顧看一下在 JavaScript 中物件取值與陣列取值的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 陣列取值
var arr = ['Ray'];
arr[0]; // Ray

var obj = {
name: 'Ray',
};
// 點運算子取值
obj.name; // Ray
// 中括號運算子取值
obj['name'];

// 中括號運算子 + 變數取值
var variable = 'name';
obj[variable]; // Ray

基本上在 JavaScript 中取值的行為主要有兩種方式:

  • 點運算子 - .
  • 中括號運算子 - []

那麼在 Python 中也是相同的嗎?如果是的話,那麼應該如何撰寫呢?讓我們看一下範例:

1
2
3
4
5
6
7
8
9
10
11
# 字串取值
lis = ['Ray']
lis[0] # 'Ray'

dic = {
'name': 'Ray',
}

dis['name'] # 'Ray'
variable = 'name'
dic[variable] # 'Ray'

你可以發現上面範例我並沒有使用點運算子來取值,原因是因為 Python 無法像 JavaScript 一樣使用點運算子的方式取值,但是 Python 有一些滿特別且好玩的函式方法可以取值,例如 get()

1
2
3
4
5
dic = {
'name': 'Ray',
}

dic.get('name', 'N/A') # 'Ray'

那這個 get() 語法有何用途呢?主要可以傳入兩個參數,第一個參數是你預計要取值的屬性名稱,第二個則是如果沒有這個屬性名稱存在時要回傳什麼東西給你,舉例來講:

1
2
3
4
5
dic = {
'name': 'Ray',
}

dic.get('qq', 'no key') # 'no key'

我們可以發現這種取值方式可以避免當字典的屬性不存在時的錯誤,而且可以客製化錯誤訊息,是不是相當棒呢?

等等,這邊好像一直都沒有提到字典屬性不存在會發生什麼事情對吧?JavaScript 中若我們取一個不存在的物件屬性時,會出現 undefined,並且還可以繼續執行:

1
2
3
4
var obj = {};

console.log(obj.myName); // undefined
console.log('我執行囉');

但是在 Python 中則會直接中斷程式碼出現 'KeyError: xxx' 的錯誤訊息:

1
2
3
4
5
6
7
8
9
name = 'myName'

dic = {
name: 'Ray',
}

print(dic[name])
print(dic['qq'])
print('我不能執行了')

KeyError

好吧,這時候你可能壓力大了,難不成我每次都要先寫好屬性名稱嗎?

壓力大

難道沒有一種方式是「當這個值不存在時,就寫入一個預設屬性與值」的方式嗎?

答案是有的!

這時候就要使用 setdefault() 這個函式方法,主要也是兩個參數,第一個參數是要設定/取得的屬性名稱,第二個參數則是如果這個屬性不存在時,要預設寫入的值。

舉例來講,假使今天這個屬性不存在時,必須出現「這是一段話」:

1
2
3
4
5
6
7
8
9
name = 'myName'

dic = {
name: 'Ray',
}

print(dic[name])
print(dic.setdefault('qq', '這是一段話'))
print('我不能執行了')

setdefault

反之,如果這個屬性已經存在於字典中,那麼它就會走原本本身的值,而不會被覆蓋:

1
2
3
4
5
6
7
8
9
10
name = 'myName'

dic = {
name: 'Ray',
'qq': '我已經存在囉'
}

print(dic[name])
print(dic.setdefault('qq', '這是一段話'))
print('我不能執行了')

setdefault

掌握 setdefault 之後你也可以用這個方式去針對 qq 增加一個字典,也就是巢狀物件的概念:

1
2
3
4
5
6
7
8
9
10
name = 'myName'

dic = {
name: 'Ray',
}

print(dic[name])
print(dic.setdefault('qq', {}))
print(dic['qq'].setdefault('sayHi', 'Hello Ray')) # 也可以寫成 dic.setdefault('qq', {}).setdefault('sayHi', 'Hello Ray')
print('我不能執行了')

相信你已經深深了解到 get()setdefault() 兩者美妙之處,那該選擇哪一個使用就留給你思考囉~

我該如何選擇

作者的話

有朋友問我的花雕醉雞卷是如何製作的,所以就順便分享一下材料,基本上就是 200cc 花雕酒、100cc 米酒(我是天味米酒,你可以考慮紅標米酒)、適量的白胡椒就這麼簡單的材料而已,如果你想加入當歸那些也是可以。

關於兔兔們

兔法無邊

Liker 讚賞 (拍手)

如果這一篇筆記文章對你有幫助,希望可以求點支持或 牡蠣 鼓勵 (ノД`)・゜・。

Liker 是一個按讚(拍手)的讚賞機制,每一篇文章最多可以按五下(拍手),按讚過程你是完全不用付費的(除非你想要每個月贊助我 :D),你只需要登入帳號就可以開始按讚。
而 Liker 會依據按讚數量分配獎金給創作者,所以如果你願意按個讚我會非常感謝你唷。

Google AD

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