---
# System prepended metadata

title: class

---

# 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
```