# class Class 提供了一種結合資料與功能的手段。建立一個 class 將會新增一個物件的型別 (type) 就像是假如我們需要生出3隻名字不同的狗,每隻狗都會叫跟走路或其他動作,然後擁有各自的身高或體重,這時就可以做一個class叫做dog,裡面寫不同的動作跟數據,方便後續的程式 class包括了三大特性: 封裝、繼承、多型 ## 封裝 封裝是指一種將抽象性函式介面的實作細節部份包裝、隱藏起來(private variables)的方法。就像是把狗的數據跟會做的事包在狗的class裡面這樣 適當的封裝,可以將物件使用介面的程式實作部份隱藏起來,不讓使用者看到,同時確保使用者無法任意更改物件內部的重要資料,若想接觸資料只能通過公開接入方法(Publicly accessible methods)的方式。它可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。 ## 基本架構 ```python= class ClassName: <statement-1> . . . <statement-N> ``` ## 簡單範例 class裡要有建構子 ```python= class Complex: def __init__(self): self.r = "r1" x = Complex() print(x.r) # r1 ``` 建構子可以加上引數;class內也可寫屬性,包括變數及函式 可以想像成在生成狗的時候就需要宣告他的體重跟身高這樣 ```python= class Dog: t = "dog" def __init__(self, height, weight): self.h = height self.w = weight def p(self): return "pet" x = Dog(3.0, 4.5) print(x.t, x.h, x.w, x.p()) #dog 3.0 4.5 pet ``` 一般來說,實例變數用於每一個實例為獨立資料 ```python= class Dog: i = 0 def __init__(self, name): self.name = name dog1 = Dog("dog1") dog2 = Dog("dog2") print(dog1.i, dog2.i) # 0 0 dog1.i = 1 print(dog1.i, dog2.i) # 1 0 ``` 但若共享的資料若涉及mutable物件,如list和dictionary,可能會產生意外的影響 ```python= class Dog: tricks = [] def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) d = Dog('Fido') e = Dog('Buddy') d.add_trick('roll over') e.add_trick('play dead') print(d.tricks) # ['roll over', 'play dead'] ``` 為了避免上述狀況,當要建立mutable物件時,可以寫進建構子裡 ```python= class Dog: def __init__(self, name): self.name = name self.tricks = [] def add_trick(self, trick): self.tricks.append(trick) d = Dog('Fido') e = Dog('Buddy') d.add_trick('roll over') e.add_trick('play dead') print(d.tricks) # ['roll over'] print(e.tricks) # ['play dead'] ``` ## 繼承 class還有繼承功能,class可使用繼承的class的屬性,而如果一個要求的屬性無法在該class中找到,則會繼續在base class中搜尋 假設我們現在擁有一個class叫做Vehicle,而現在我們要建立一個新的class叫做Car。因為汽車(Car)在概念意義上是交通工具(Vehicle)的一種,而且Car具備Vehicle所需要的要素,且還需要其他要素,則可使用繼承來簡化程式 表格化來看的話,如果未使用繼承,整替架構會長這樣 ![](https://i.imgur.com/qMaUK2S.jpg) 而如果使用了繼承,整體架構可變這樣 ![](https://i.imgur.com/1Gzn6xA.png) 相對來講就簡單許多 基礎架構如下 ```python= class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> ``` class也可繼承多個class,假如有一個屬性在該class沒有被找到,則會在Base1搜尋它,接著(遞迴地)在Base1的base class中搜尋,假如在那裡又沒有找到的話,會在Base2搜尋,依此類推,基礎架構如下 ```python= class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N> ``` 以下是簡單範例,Cat繼承了Dog,可使用Dog的屬性 ```python= class Dog: def __init__(self, name): self.name = name self.tricks = [] def add_trick(self, trick): self.tricks.append(trick) class Cat(Dog): def __init__(self, name): super().__init__(name) super().add_trick("cat") cat = Cat("name") print(cat.name, cat.tricks) # name ['cat'] ``` ## 多型 多型是指如果該class和繼承的class中有相同名稱的屬性時會有的特性,包括過載Overload和覆寫Override ### 過載Overload 是指一個類裡面(包括父類的方法)存在屬性名相同,但是引數不一樣的屬性,引數不一樣可以是不同的引數個數、型別或順序 ### 覆寫Override 是指子類中存在和父類相同的屬性,屬性名稱和引數相同(個數&型別&順序一致),此時以子類的屬性為優先 以下為簡單範例,假設貓是動物的一種,但動物跟貓都會叫不過叫聲都不同,這時就可以將Cat的talk和name複寫到Animal上面 ```python= class Animal: def __init__(self, name): self.name = name def talk(self): print("woof") class Cat(Animal): def __init__(self, name): super().__init__(name) def talk(self): print("meow") cat = Cat("name") print(cat.name) # name cat.talk() # meow ```