從 JavaScript 角度學 Python(23) - Class

前言

接下來算是小聊一下 Python 的 Class 語法而已,算是稍微休息一下,所以這邊簡單聊就好。

物件導向

Python 本身就是一個物件導向語言(Object-oriented programming,就是俗稱的 OOP),那麼什麼是物件導向呢?JavaScript 也是物件導向語言嗎?

好吧,這個問題其實困擾我很久,所以前面就稍微簡單聊一下這個東西,畢竟有些人說 JavaScript 是物件導向語言也有人家說 JavaScript 不是物件導向的語言,那麼到底什麼是物件導向呢?與其繞口令不如翻一些文件來說明,先讓我們看一下維基百科對於「物件導向程式設計」的解釋是什麼:

它可能包含資料、特性、程式碼與方法。物件則指的是類別(class)的實例。它將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充性,物件裡的程式可以存取及經常修改物件相關連的資料。在物件導向程式程式設計裡,電腦程式會被設計成彼此相關的物件。

相信這邊你應該看得非常混淆,畢竟描述的很文鄒鄒:

假裝聽得懂的我

但是你可能會想說「物件導向,那這個是不是跟物件有關係啊?」

答案是「沒錯,確實有關係」,就是與物件有很大的關聯性,除此之外通常物件導向的開發會是以一個類別去做宣告,也就是 class,而這個 class 通常是一個抽象的物件,舉例來講會有一個狗的類別,然這個狗的類別會具有牠所有的基本特徵,所以寫法可能是這樣:

1
2
3
4
5
6
7
8
9
10
class 狗
public 方法:
吼叫()
指甲:
private 方法:
毛色:
品種:
尾巴長度:
名字:
學名:

(我絕對不會說我是看著我家的狗在想上面的 class)

而我們可以透過上面的狗類別一直繼承下來給予其他特有的名稱,例如:毛色是棕色、品種是米克斯、尾巴無等等,所以你可以把物件導向也想像成它是一個藍圖概念。

依照上面的說法來講,所以 JavaScript 也是物件導向語言囉?答案基本上是的,JavaScript 確實也是物件導向語言,但是 JavaScript 是以 原型基礎為導向 的物件導向語言,我相信你看到這邊已經感覺像是繞口令了,所以接下來就來看一些範例好了。

以下是 Java 語言,我隨便簡單寫出來參考比較而已:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Home
{
public String father;
public String mother;
Home(String a, String b) {
father = a;
mother = b;
}
public void myFather(){
System.out.println("my father is " + father);
}
}

public class main
{
public static void main(String[] args)
{
Home myHome = new Home("jack", "judy");
System.out.println(myHome.father); // jack
System.out.println(myHome.mother); // mother
myHome.myFather(); // my father is jack
}
}

但是如果將上面程式碼改寫成 JavaScript 的話則是以下:

1
2
3
4
5
6
7
8
9
10
11
12
function Home(father, mother) {
this.father = father;
this.mother = mother;
}

Home.prototype.myFather = function() {
console.log('my father is ' + this.father);
}

const myHome = new Home('Jack', 'Judy');
console.log(myHome.father, myHome.mother);// Jack Judy
myHome.myFather(); // my father is jack

至於為什麼挑 Java 當作範例就不多說明了(笑)。

你知道 Java 跟 JavaScript 的差異嗎?

基本上我們可以看到 Java 就是以 class 去抽象化一個東西、一個概念,而這個 class 可以一直被繼承。

但是前面範例中我們也有看到 JavaScript 也可以做到類似的事情,難道這樣子 JavaScript 還不算是一個物件導向語言嗎?事實上 JavaScript 不太適合直接說它是 OOP,而是比較適合叫 OOJS(Object-oriented JavaScript),畢竟 JavaScript 是以原型為基礎的語言,詳情可以在閱讀 MDN 文件中的 初學者應知道的物件導向 JavaScript 章節會有更好的描述,而前面這邊只是小小的簡單聊一下而已,並沒有打算過度深入談論這一塊。

oh!題外話一下,雖然 JavaScript 在 ECMAScript6 引入了 class 語法,但本質上那只是 prototype 的語法糖而已,實際上 JavaScript 在底層的運作依然是屬於原型,千萬不要被 JavaScript 的 class 語法糖給騙了唷~

Class

前面有提到 Python 本身就是一個 OOP 語言也有講到什麼是物件導向的概念之後,接下來我們也要來認識一下 Python 要如何建立屬於自己的 class,有寫過 JavaScript class 語法的人基本上會感覺很熟悉或者格外親切,因為語法上的使用非常的雷同:

1
2
class Home:
father = 'jack'

這邊我們也可以簡單看一下 JavaScript 的 class

1
2
3
class Home {
father = 'jack'
}

是不是覺得兩者相似度很高呢?感到格外親切了呢?

分不清楚什麼是 Python 什麼是 JavaScript 了

那麼 Python 該如何實例化剛剛宣告的 class 呢?你不知道什麼是實例化嗎?簡單來講你可以把它當作你給了這個抽象的東西一個生命的概念,所以讓我們來看一下怎麼該實例化:

1
2
3
4
5
class Home:
father = 'jack'

myHome = Home()
print(myHome.father) # jack

有沒有覺得超級簡單呢?連 new 都省略了呢!是不是覺得超級簡潔呢?(JavaScript 必須使用 new 才可以。)

我不用 new

但是這時候你可以會想說建立一個 class 的時候,我們都會需要傳入一些參數告知目前 class 的一些初始值或者樣貌,以狗來舉例的話,你給予狗生命時總要給牠毛色與品種吧?

那麼以 JavaScript 的寫法來講的話就會像這樣:

1
2
3
4
5
6
7
class Home {
constructor(father) {
this.father = father;
}
}
const myHome = new Home('jack');
console.log(myHome.father); // jack

那麼 Python 呢?Python 也是用 constructor 嗎?Python 主要是用 __init__ 函式,所有的 class 都會有一個 __init__,就跟 JavaScript 一樣,所有的 class 都有一個 constructor 可以使用,所以寫法就會變成以下:

1
2
3
4
5
6
class Home:
def __init__ (self, father):
self.father = father

myHome = Home('jack')
print(myHome.father) # jack

此刻的你應該會覺得很疑惑 __init__ 中的 self 是什麼東西?你可以把它想像成 JavaScript 中 constructorthis,但是千萬不要把 Python 中的 self 跟 JavaScript 的 self 搞混,剛好前陣子我有寫一篇關於 JavaScript 的 self 是什麼的文章,也推薦給你參考看看。

好,拉回 Python 中,所以 Python 的 self 會指向 class Home 本身,而每次建立一個 class 的時候,Python 都會自動呼叫 __init__ 函式(JavaScript 也是相同的,它會自動呼叫 constructor 函式)基本上你也不一定要宣告為 self 你也可以叫別的,例如:hello or ray 等等:

1
2
3
4
5
6
class Home:
def __init__ (ray, father):
ray.father = father

myHome = Home('jack')
print(myHome.father) # jack

但是我建議還是保留叫 self 會比較好,如果你取名一個很奇怪的名字,可能會被約出去喝咖啡。

那麼接下來也是與 Java 類似,我們會將函式方法寫在 class 中,但是這邊要注意,請記得替該方法傳入 self,否則你會無法呼叫 class 本身的屬性:

1
2
3
4
5
6
7
8
9
class Home:
def __init__ (self, father):
self.father = father
def myFather(self):
print('my father is ' + self.father)

myHome = Home('jack')
print(myHome.father) # jack
myHome.myFather() # my father is jack

當如果你想修改 Home 中的屬性的話,也非常的簡單,就跟修改物件屬性的方式相同:

1
2
3
4
5
6
7
8
9
10
11
class Home:
def __init__ (self, father):
self.father = father
def myFather(self):
print('my father is ' + self.father)

myHome = Home('jack')
print(myHome.father) # jack
myHome.myFather() # my father is jack
myHome.father = 'Ray'
print(myHome.father) # Ray

其他的還有刪除 class 中的屬性與方法等等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Home:
def __init__ (self, father):
self.father = father
def myFather(self):
print('my father is ' + self.father)

myHome = Home('jack')
print(myHome.father) # jack
myHome.myFather() # my father is jack

myHome.father = 'Ray'
print(myHome.father) # Ray

del myHome.father
del myHome

但是這邊要注意,當刪除了之後就無法再被呼叫囉。

最後一種寫法是比較特別的狀況,通常來講 class 內容是不能為空的,如果是特殊狀況而需要寫一個空的 class 是可以使用 pass 語法來避免出錯:

1
2
class Home:
pass

否則正常來講你可能會得到「IndentationError: expected an indented block」這個錯誤。

那麼這一章節都已經提到 class,那麼下一章節就閃不掉所謂的繼承啦~

參考文獻

作者的話

最近 Mac 好像在發神經一樣,莫名其妙就會準備飛機起飛,難道…它在暗示我該換 M1 了嗎 QQ?

關於兔兔們

兔法無邊

Liker 讚賞

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

Buy Me A Coffee Buy Me A Coffee

Google AD

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