---
# System prepended metadata

title: Python物件導向，淺談class
tags: [人生苦短，我學Python]

---

# Python物件導向，淺談class
###### tags: `人生苦短，我學Python`


## 物件導向概述
:::success
:sign_of_the_horns: 物件導向程式設計(Object Oriented Programming)簡稱OOP，主要針對大型軟體設計而提出，它可以使軟體設計更加靈活。
:::
   
## 對象
:::warning
這是一個抽象的概念，世間萬物皆為對象。比如一個人。
:::
* 靜態被分為屬性，任何對象都具備自身屬性，如人的性別
* 動態表示對方的行為，及對象執行的動作，孺人可以行走
* 在Python中，一切都是對象。
   
## 類
:::danger
類是封裝對象的屬性及行為的載體，反過來說，具有相同屬性和行為的類實體被稱為類
:::
  
  
## OOP設計特點
:::success
1. 封裝
    將對象的屬性和行為封裝起來，而將對象的屬性和行為封裝起來的截體就是類，類通常對客戶隱藏實際細節。

2. 繼承
    子能繼承父的特性，但無法反過來

3. 多態
    一個父親能有多個兒子，兒子能繼承父親的技能，但兒子們也有自身的技能
:::

## 定義類
* 在Python中，類的定義使用class關鍵字
```python=
class Classname:
    statement
```

* 也可以先用pass卡著
```python=
class Geese:
    pass
```

## 創建類的實例
```python=
classname(parameterlist)
```
* 比如，定義一個Geese類的實例
```python=
wildGeese = Geese()
print(wildGeese)
# <__main__.Geese object at 0x000002AD716B4DC0>
```

## 建立__init__方法

:::warning
:warning: __init__是一個特殊的方法，每當創建一個新實例的時候，Pytho都會自動執行它。
:warning: __init__方法必須包含一個self參數，而且必須是第一個參數。
:warning: self參數是指象實例本身的引用，屬於訪問類的屬性和方法。
:warning: 在方法要調用時會傳遞實際self參數。
:warning: 因此，當__init__方法只有一個參數時，就不需要只掉參數。
:::

* 舉例
```python=
class Geese:
    def __init__(self):
        print("我是大雁類")

wildGeese = Geese()
# 我是大雁類

# 如果把self拿掉便會出現......
# TypeError: __init__() takes 0 positional arguments but 1 was given
# 所以說還是需要self的
```

* 其實還可以自定義參數
```python=
class Geese:
    def __init__(self, beak, wing, claw):
        print("我是大雁類")
        print(beak)
        print(wing)
        print(claw)

beak_1 = "嘴"
wing_1 = "翅膀"
claw_1 = "爪子"
wildGeese = Geese(beak_1, wing_1, claw_1)

# 我是大雁類
# 嘴
# 翅膀
# 爪子
```

# 創建類的成員並使用
* 創建實例方法並使用
```python=
def funcName(self, parametetlist):
    block 
```

* 創建方法完成後，可以透過類的實例名稱和"."操作符進行呼叫
```python=
instanceName.functionName(parametervalue)
```
* 實際創造一個函數(fly())
```python=
class Geese:
    def __init__(self, beak, wing, claw):
        print("我是大雁類")
        #print(beak)
        #print(wing)
        #print(claw)
    def fly(self, state):
        print(state)

beak_1 = "嘴"
wing_1 = "翅膀"
claw_1 = "爪子"
wildGeese = Geese(beak_1, wing_1, claw_1)
wildGeese.fly("我可以直衝雲霄")
# 我是大雁類
# 我可以直衝雲霄
```

* 創建數據成員並使用
```python=
class Geese:
    
    neck = "脖子"
    
    def __init__(self):
        print(self.neck)

geese = Geese()
# 脖子

# 此寫法等同於

self.neck = "脖子"
print(self.neck)
```

## 訪問限制
:::success
:warning: _foo : 以單下滑線開頭表示protect(保護)類型的，只允許子類及本身，不能使from module import  * 語句導入。

:warning: __foo():雙下滑線到表私有，只允許定義該方法的類本身進行訪問，可以透過"類的實例名.類名.__xxx"方式訪問。

:warning: __foo__():代表特殊定義方法，一般表示系統定義名字，比如:__init__()
:::

## 屬性
* 建立用於計算的屬性
```python=
@property
def methodname(self):
    block
```
* 實際運用
```python=
class Reat:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    @property
    def area(self):
        return self.width * self.height

print(Reat(100, 200).area)
# 2000
```
:::info
:page_with_curl: 轉化成屬性後，就可以直接透過方法名來訪問方法，就不需要添加小括號
:::

* 添加安全保護機制
```python=
# 想要建立可以讀取但不能相改的屬性可以用@property實踐
class TVshow:
    def __init__(self, show) :
        self.__show = show
    
    @property
    def show(self):
        return self.__show
    
tsShow = TVshow("正在播放<<8964>>")
print("默認: ", tsShow.show)
# 默認:  正在播放<<8964>>
# 那如果改成...

# tsShow = "正在播放<<爆打粉紅>>" 修改屬性值
# print("修改後: ", tsShow.show) 獲取屬性值

# AttributeError: 'str' object has no attribute 'show'
```

## 繼承
* 簡單明瞭，就是繼承父類
```python=
class className(baseclasslist):
    Statement
```

* 比如說....
```python=
class fruit:
    color = "green"
    
    def fruitcolor(self, color):
        print("水果顏色是: ", color)
        print("-----------")
        print("原本的顏色是: ", fruit.color)

class Apple(fruit):
    color = "red"
    
    def __init__(self):
        print("我是蘋果")

Apple().fruitcolor(Apple.color)

# 我是蘋果
# 水果顏色是:  red
# -----------
# 原本的顏色是:  green
```

## 方法重寫
:::success
:heavy_check_mark: 在自身認為不適用的情況下，對def內部的函式內容作改寫。
:::

## 使用super()呼叫父類
:::success
:brain: 如果要在子類中引用父類的方法，但是又需要新增一些子類所特有的內容，可通過類名.方法()和super()來呼叫父類的方法，再個性化子類的對應函式。
:scream: 直接使用類名.方法()來呼叫時，還是需要傳入self為第一個引數，而使用super()呼叫則python自動將self傳入，因此使用super()比較簡潔。

:mega: 好處
:bug: 簡化程式碼
:bug: 如果父類名稱修改，對其他呼叫父類函式的類來說也沒有影響，而如果直接使用父類的名稱來呼叫，父類名稱修改會影響其他所有的類。
:::

* 舉例
```python=
class drive:
    def __init__(self, name):
        self.name = name
    def getname(self):
        return self.name
    def displayVehicle(self):
        print("廠牌: ", self.name)

class car(drive):
    def __init__(self, name, mod):
        super().__init__(name) # 呼叫父類建構函式
        self.mod = mod
    def display(self):
        print(self.getname())
        print(self.mod)

drive("BMV").displayVehicle()
car("totya", "GH333").display()
# 廠牌:  BMV
# totya
# GH333
```

{%hackmd S1DMFioCO %}