# 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所需要的要素,且還需要其他要素,則可使用繼承來簡化程式
表格化來看的話,如果未使用繼承,整替架構會長這樣

而如果使用了繼承,整體架構可變這樣

相對來講就簡單許多
基礎架構如下
```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
```