从 JavaScript 角度学 Python(23) - Class

前言

接下来算是小聊一下 Python 的 Class 语法而已,算是稍微休息一下,所以这边简单聊就好。

物件导向

Python 本身就是一个物件导向语言(Object-oriented programming,就是俗称的 OOP),那麽什麽是物件导向呢?JavaScript 也是物件导向语言吗?

好吧,这个问题其实困扰我很久,所以前面就稍微简单聊一下这个东西,毕竟有些人说 JavaScript 是物件导向语言也有人家说 JavaScript 不是物件导向的语言,那麽到底什麽是物件导向呢?与其绕口令不如翻一些文件来说明,先让我们看一下维基百科对於「物件导向程序设计」的解释是什麽:

它可能包含资料、特性、程序码与方法。物件则指的是类别(class)的实例。它将物件作为程序的基本单元,将程序和资料封装其中,以提高软件的重用性、灵活性和扩充性,物件里的程序可以存取及经常修改物件相关连的资料。在物件导向程序程序设计里,电脑程序会被设计成彼此相关的物件。

相信这边你应该看得非常混淆,毕竟描述的很文邹邹:

https://ithelp.ithome.com.tw/upload/images/20210922/20119486jPypwjQjBQ.png

但是你可能会想说「物件导向,那这个是不是跟物件有关系啊?」

答案是「没错,确实有关系」,就是与物件有很大的关联性,除此之外通常物件导向的开发会是以一个类别去做宣告,也就是 class,而这个 class 通常是一个抽象的物件,举例来讲会有一个狗的类别,然这个狗的类别会具有牠所有的基本特徵,所以写法可能是这样:

class 狗
  public 方法:
    吼叫()
    指甲:
  private 方法:
    毛色:
    品种:
    尾巴长度:
    名字:
    学名:

(我绝对不会说我是看着我家的狗在想上面的 class)

而我们可以透过上面的狗类别一直继承下来给予其他特有的名称,例如:毛色是棕色、品种是米克斯、尾巴无等等,所以你可以把物件导向也想像成它是一个蓝图概念。

依照上面的说法来讲,所以 JavaScript 也是物件导向语言罗?答案基本上是的,JavaScript 确实也是物件导向语言,但是 JavaScript 是以 原型基础为导向 的物件导向语言,我相信你看到这边已经感觉像是绕口令了,所以接下来就来看一些范例好了。

以下是 Java 语言,我随便简单写出来参考比较而已:

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 的话则是以下:

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 当作范例就不多说明了(笑)。

https://ithelp.ithome.com.tw/upload/images/20210922/201194862Pr7UjgqL2.png

基本上我们可以看到 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 语法的人基本上会感觉很熟悉或者格外亲切,因为语法上的使用非常的雷同:

class Home:
  father = 'jack'

这边我们也可以简单看一下 JavaScript 的 class

class Home {
  father = 'jack'
}

是不是觉得两者相似度很高呢?感到格外亲切了呢?

https://ithelp.ithome.com.tw/upload/images/20210922/20119486JWPk05LeuQ.png

那麽 Python 该如何实例化刚刚宣告的 class 呢?你不知道什麽是实例化吗?简单来讲你可以把它当作你给了这个抽象的东西一个生命的概念,所以让我们来看一下怎麽该实例化:

class Home:
  father = 'jack'

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

有没有觉得超级简单呢?连 new 都省略了呢!是不是觉得超级简洁呢?(JavaScript 必须使用 new 才可以。)

https://ithelp.ithome.com.tw/upload/images/20210922/20119486oM37nhUu8y.png

但是这时候你可以会想说建立一个 class 的时候,我们都会需要传入一些参数告知目前 class 的一些初始值或者样貌,以狗来举例的话,你给予狗生命时总要给牠毛色与品种吧?

那麽以 JavaScript 的写法来讲的话就会像这样:

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 可以使用,所以写法就会变成以下:

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 等等:

class Home:
  def __init__ (ray, father):
    ray.father = father

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

但是我建议还是保留叫 self 会比较好,如果你取名一个很奇怪的名字,可能会被约出去喝咖啡。

那麽接下来也是与 Java 类似,我们会将函式方法写在 class 中,但是这边要注意,请记得替该方法传入 self,否则你会无法呼叫 class 本身的属性:

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 中的属性的话,也非常的简单,就跟修改物件属性的方式相同:

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 中的属性与方法等等:

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 语法来避免出错:

class Home:
  pass

否则正常来讲你可能会得到「IndentationError: expected an indented block」这个错误。

那麽这一章节都已经提到 class,那麽下一章节就闪不掉所谓的继承啦~

参考文献

作者的话

最近 Mac 好像在发神经一样,莫名其妙就会准备飞机起飞,难道...它在暗示我该换 M1 了吗 QQ?

关於兔兔们

兔法无边


<<:  每个人都该学的30个Python技巧|技巧 24:超便利的内建函式—max()、min()、sum()(字幕、衬乐、练习)

>>:  TailwindCSS 从零开始 - 让 Variants 成为伪类的强大神器

06 APCS 考试内容 Overview

APCS 分为两个大部分,观念题和实作题。观念题以选择题为主,旨在测试考生对於程序语言的观念是否正确...

【C#】Fibonacci

简单来说~费氏数列从0 and 1开始~ 後面的每一个数~ 都是前两数相加得来~ 例如,0,1,1,...

Day-21 Excel位址精选题目练习

经过了一晚的思考,我认为昨天讲的还不够清楚,因此今天我找了三题相关题目,今天来为大家做更深入的讲解,...

JS 物件的参考特性 DAY59

JS 在将值赋予到变数上时 会有两个特性(Call by value(传值) 与 Call by r...