从 JavaScript 角度学 Python(9) - 容器型别(上)

前言

BMI 作业做得还好吗?不知道你有没有完成,但是这边时间不等人,所以我们就接着聊聊下一个主题吧。

什麽是容器型别?

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

其实在 JavaScript 中这个型别就是所谓的物件型别,只是在 Python 则是称为容器型别。

那...什麽是容器型别呢?容器型别你可以把它想像成类似使用一个个的马克杯或者是盒子,可以用来放各种东西,就如同前面所言以 JavaScript 来讲的容器型别就是阵列与物件:

// 阵列 Array
var arr = ['Ray'];

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

(这边要注意阵列依然是属於物件型别,原因可详见:JavaScript 核心观念(31)-物件-阵列)

那麽在 Python 上并不称为阵列与物件,而是串列(List) 与字典 (Dictionaries):

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

那麽写法会不会有差异呢?基本上串列是没有什麽太大的差异,但是在字典上就会有一点差异:

# 串列
lis = ['Ray']

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

为什麽会说字典有一点差异呢?先让我们转过来看一下 JavaScript 的部分,在 JavaScript 的物件属性我们是可以省略单/双引号的:

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

但是在 Python 是不能省略这个单/双引号的,否则你是会出现 NameError: name 'name' is not defined 的唷~

https://ithelp.ithome.com.tw/upload/images/20210909/20119486WBfAJQlemP.png

那麽由於这个错误讯息的关系,代表着没有使用单/双引号包覆的这个属性,是会被 Python 认为这是 一个变数

因此我们可以大胆的尝试与挑战一件事情,假使我宣告了一个变数并且字典的属性不用使用单/双引号包覆,其结果又会如何呢?

name = 'myName'

dic = {
  name: 'Ray',
}

print(dic) # ??

我们可以看到结果是可以正常运作的:

https://ithelp.ithome.com.tw/upload/images/20210909/20119486l97Q6Ef5ea.png

是不是发现与我们原本的 JavaScript 有些许不同呢?在一般的情况下 JavaScript 是不允许你将物件的属性变成变数传入的,不管怎样都会被 JavaScript 转换成一个字串属性,只是在呈现与撰写上它会忽略并允许你这样做而已:

var name = 'myName';

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

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

但是如果真的有这种例外状况,也就是物件的属性是必须透过变数所传入的话,其实也可以做到,这时候你所必须使用是中括号运算子来达到这个需求:

var name = 'myName';

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

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

See!效果基本上是一样的,只是变成另一种呈现方式罢了。

容器型别取值

接下来就是容器取值,串列取值与字典取值的部分会与原本的 JavaScript 也有差别吗?先让我们回顾看一下在 JavaScript 中物件取值与阵列取值的方式:

// 阵列取值
var arr = ['Ray'];
arr[0]; // Ray

var obj = {
  name: 'Ray',
};
// 点运算子取值
obj.name; // Ray
// 中括号运算子取值
obj['name'];

// 中括号运算子 + 变数取值
var variable = 'name';
obj[variable]; // Ray

基本上在 JavaScript 中取值的行为主要有两种方式:

  • 点运算子 - .
  • 中括号运算子 - []

那麽在 Python 中也是相同的吗?如果是的话,那麽应该如何撰写呢?让我们看一下范例:

# 字串取值
lis = ['Ray']
lis[0] # 'Ray'

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

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

你可以发现上面范例我并没有使用点运算子来取值,原因是因为 Python 无法像 JavaScript 一样使用点运算子的方式取值,但是 Python 有一些满特别且好玩的函式方法可以取值,例如 get()

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

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

那这个 get() 语法有何用途呢?主要可以传入两个参数,第一个参数是你预计要取值的属性名称,第二个则是如果没有这个属性名称存在时要回传什麽东西给你,举例来讲:

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

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

我们可以发现这种取值方式可以避免当字典的属性不存在时的错误,而且可以客制化错误讯息,是不是相当棒呢?

等等,这边好像一直都没有提到字典属性不存在会发生什麽事情对吧?JavaScript 中若我们取一个不存在的物件属性时,会出现 undefined,并且还可以继续执行:

var obj = {};

console.log(obj.myName); // undefined
console.log('我执行罗');

但是在 Python 中则会直接中断程序码出现 'KeyError: xxx' 的错误讯息:

name = 'myName'

dic = {
  name: 'Ray',
}

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

https://ithelp.ithome.com.tw/upload/images/20210909/201194867FX5YCtzNq.png

好吧,这时候你可能压力大了,难不成我每次都要先写好属性名称吗?

https://ithelp.ithome.com.tw/upload/images/20210909/201194864yBTyPtNi4.png

难道没有一种方式是「当这个值不存在时,就写入一个预设属性与值」的方式吗?

答案是有的!

这时候就要使用 setdefault() 这个函式方法,主要也是两个参数,第一个参数是要设定/取得的属性名称,第二个参数则是如果这个属性不存在时,要预设写入的值。

举例来讲,假使今天这个属性不存在时,必须出现「这是一段话」:

name = 'myName'

dic = {
  name: 'Ray',
}

print(dic[name])
print(dic.setdefault('qq', '这是一段话'))
print('我不能执行了')

https://ithelp.ithome.com.tw/upload/images/20210909/20119486NDaqRAYtWi.png

反之,如果这个属性已经存在於字典中,那麽它就会走原本本身的值,而不会被覆盖:

name = 'myName'

dic = {
  name: 'Ray',
  'qq': '我已经存在罗'
}

print(dic[name])
print(dic.setdefault('qq', '这是一段话'))
print('我不能执行了')

https://ithelp.ithome.com.tw/upload/images/20210909/20119486DrsgYSIOQ2.png

掌握 setdefault 之後你也可以用这个方式去针对 qq 增加一个字典,也就是巢状物件的概念:

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() 两者美妙之处,那该选择哪一个使用就留给你思考罗~

https://ithelp.ithome.com.tw/upload/images/20210909/201194863q7pszlvQW.png

作者的话

有朋友问我的花雕醉鸡卷是如何制作的,所以就顺便分享一下材料,基本上就是 200cc 花雕酒、100cc 米酒(我是天味米酒,你可以考虑红标米酒)、适量的白胡椒就这麽简单的材料而已,如果你想加入当归那些也是可以。

关於兔兔们

兔法无边


<<:  [Day9]C# 鸡础观念- 省去重复程序码的好帮手~for回圈

>>:  [iT铁人赛Day10]JAVA程序结构

系统分析师的养成之路—如何培养商业思维篇

前一篇我分享了系统分析师必须要有商业思维才有能力真正对你所服务的客户提供正确且完整的解决方案!但是,...

D19: 工程师太师了: 第10话

工程师太师了: 第10话 杂记: 前阵子朋友传了一个社团给我, 里面大致是一个武林盟主的设计, 想要...

「ASP.NET 具有潜在危险 request.form 的值」...有无危险实例可参

最近写一个ASP.NET WebForm的网页,允许使用者在TextBox输入各种文字,也包括Htm...

DAY26 CNN(卷积神经网路)

昨天介绍完浅层神经网路程序,今天要来研究CNN卷积神经网路: 卷积神经网路由一个或多个卷积层和顶端的...

每日挑战,从Javascript面试题目了解一些你可能忽略的概念 - Day5

tags: ItIron2021 Javascript 作者碎碎念 当时在用这一系列题目跑模拟面试活...