---
title: Python 入門教學-物件導向
tags: python_beginner
---
# 物件導向
## Module 模組
Module其實很簡單,一個py檔就是一個Module
我們可以建立一個天氣預報的主程式`weatherman.py`,使用`import`引入一個叫做`report.py`的模組
```python=
# weatherman.py
import report
description = report.get_description()
print("Today's weather:", description)
```
```python=
# report.py
def get_description():
'''return random weather, just like the pros'''
from random import choice
possibilities = ['rains', 'snow', 'sleet', 'fog', 'sun', 'who knows']
return choice(possibilities)
```
注意這裡report也import了random模組,但是方式不一樣,引入模組的方式有以下幾種
* import: 直接把整個Module都引入進來
* from ... import ...: 從某個Module引入某個東西,例如我們在`report.py`中,從random引入了choice這個function,我們也可以從`report.py`引入get_description()
```python=
# weatherman.py
from report import get_description
description = get_description()
print("Today's weather:", description)
```
注意這邊,我們使用get_description()的方式跟上面不一樣。上面的用法是,我們把整個report都印入,並且要使用report內的get_description()這個function。但是在這邊,我們直接把get_description()引入進來,就不需特別指定是從哪個Module來的
* import ... as ...: 把引入進來的東西取一個別名,方便使用
```python=
# weatherman.py
import report as wr
description = get_description()
print("Today's weather:", description)
```
## Package 套件
可以把很多個Module放在同一個資料夾中,就變成一個Package,例如這邊我們可以建立一個叫做sources的資料夾,裡面放有`daily.py`,以及`weekly.py`兩個Module,分別作為每日預報以及每周預報使用。只是要特別注意,sources裡面要有一個叫做`__init__.py`的檔案存在,python才會將這個資料夾視為Package
```python=
# sources/daily.py
def forecast():
'''fake daily forecast'''
return 'like yesterday'
```
```python=
# sources/weekly.py
def forecast():
'''Fake weekly forecast'''
return ['snow', 'more snow', 'sleet', 'freezing rain', 'rain', 'fog', 'hail']
```
我們的`weatherman.py`主程式,就要使用from package import module的方式引入Package裡面的Module
```python=
# weatherman.py
from sources import daily, weekly
print("Daily forecast:", daily.forecast())
print("Weekly forecast:")
for number, weather in enumerate(weekly.forecast(), 1):
print(number, weather)
```
## Class 類別
所有東西都是物件,你也可以用class來定義自己的物件。class就像一個模型,印出來的東西就是物件。這邊定一個Person的class
```python=
# 定義Person類別
class Person():
# __init__是建構子,self是類別當中每個function都要加入的,name是建構子的參數
def __init__(self, name):
self.name = name
def speak(self, words):
print("{}says, {}".format(self.name, words))
# 使用Person類別建立物件
hunter = Person('Sam') # 傳入建構子的name參數'Sam'
print(hunter.name)
```
### 繼承
類別是可以繼承的,子類別可使用父類別有的所有東西
```python=
class Person():
# __init__是建構子,self是類別當中每個function都要加入的,name是建構子的參數
def __init__(self, name):
self.name = name
def speak(self, words):
print("{} says, {}.".format(self.name, words))
class Hunter(Person):
def __init__(self, name, gun):
super().__init__(name) # 在類別中使用父類別的function,要使用super()代表父類別
self.gun = gun
hunter = Person('Sam')
hunter.speak('I have a dream') # 可直接使用父類別的function
```
### Override
Override屬於多型(polymorphism)的一種,子類別可用同樣名稱的function來覆蓋掉父類別的function
```python=
class Person():
# __init__是建構子,self是類別當中每個function都要加入的,name是建構子的參數
def __init__(self, name):
self.name = name
def speak(self, words):
print("{} says, {}.".format(self.name, words))
class Hunter(Person):
def __init__(self, name, gun):
super().__init__(name) # 在類別中使用父類別的function,要使用super()代表父類別
self.gun = gun
def speak(self, words):
print("{} put his {} in your head, and says, {}".format(self.name, self.gun, words))
hunter = Hunter('Sam', 'shotgun')
hunter.speak('I have a dream') # 可直接使用父類別的function
```
## LAB1 - 實作 Fraction Class
:::info
參考: [1.13.Python中面向对象编程:定义类]()
:::
實作一個分數的類別,來體會class可以做甚麼事情
```python=
# -*- coding: UTF-8 -*-
'''
Author: Sam Yang
Description: This program is a practice from:
http://interactivepython.org/runestone/static/pythonds/Introduction/ObjectOrientedProgramminginPythonDefiningClasses.html#a-fraction-class
it is aim to simulate a fraction by implement the show, +, -, *, /, equal, less than, great than method of fraction.
'''
# find GCD = greatest common divisor, using Euclid's Algorithm
# http://www.csie.ntnu.edu.tw/~u91029/Divisor.html#4
def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
class Fraction:
def __init__(self,top,bottom):
self.num = top
self.den = bottom
# override the built-in str method, the original one will return the reference of the instance
def __str__(self):
return str(self.num) + "/" + str(self.den)
# override the built-in add method
def __add__(self, otherFraction):
newNum = self.num * otherFraction.den + self.den * otherFraction.num
newDen = self.den * otherFraction.den
common = gcd(newNum, newDen)
return Fraction(newNum//common, newDen//common)
# override the built-in sub method
def __sub__(self, otherFraction):
newNum = self.num * otherFraction.den - self.den * otherFraction.num
newDen = self.den * otherFraction.den
common = gcd(newNum, newDen)
return Fraction(newNum//common, newDen//common)
# override the built-in mul method
def __mul__(self, otherFraction):
newNum = self.num * otherFraction.num
newDen = self.den * otherFraction.den
common = gcd(newNum, newDen)
return Fraction(newNum//common, newDen//common)
# override the built-in div method
def __truediv__(self, otherFraction):
newNum = self.num * otherFraction.den
newDen = self.den * otherFraction.num
common = gcd(newNum, newDen)
return Fraction(newNum//common, newDen//common)
# override the built-in eq method, the original one is shallow equality, that means, only when the two object has sam reference, will return true
def __eq__(self, otherFraction):
firstNum = self.num * otherFraction.den
secondNum = otherFraction.num * self.den
return firstNum == secondNum
# override the built-in lt method
def __lt__(self, otherFraction):
firstNum = self.num * otherFraction.den
secondNum = otherFraction.num * self.den
return firstNum < secondNum
# override the built-in gt method
def __gt__(self, otherFraction):
firstNum = self.num * otherFraction.den
secondNum = otherFraction.num * self.den
return firstNum > secondNum
f1 = Fraction(2,5)
f2 = Fraction(2,4)
f3 = f1 / f2
print(f3)
```
## LAB2 - 實作 Queue
:::info
參考: [3.12.Python实现队列](https://facert.gitbooks.io/python-data-structure-cn/3.%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/3.12.Python%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/)
:::
* 什麼是Queue(佇列)
一種先進先出(FIFO, First In First Out)的資料結構,例如排隊等買票就是一種Queue的概念

* Queue包含以下動作
* isEmpty(): 回傳這個queue是不是空的
* enqueue(item): 將項目放入queue最後端
* dequque(): 將項目從queue最前端取出
* size(): 回傳queue裡面有多少項目
* Queue程式碼
```python=
# queue.py
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0, item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
```
嘗試看看能不能用
```python=
>>> q.size()
3
>>> q.isEmpty()
False
>>> q.enqueue(8.4)
>>> q.enqueue(4)
>>> q.enqueue('dog')
>>> q.size()
3
>>> q.dequeue()
8.4
>>> q.dequeue()
4
```
### 利用queue解決燙手山芋問題
* 問題描述
一群人圍著一個圈,盡可能快速地傳遞手上的山芋,一段時間後,手上拿著山芋的人出局,最後一個留在場上的人獲勝

* 解法
利用queue來模擬這個情境,queue最前端的人是正拿著山芋的人,傳到下一個人時,只要把自己移動到queue最尾端就可以了

* 程式碼
```python=
from queue import Queue
def hotPotato(namelist, num):
# 將所有人放入queue中
simqueue = Queue()
for name in namelist:
simqueue.enqueue(name)
while simqueue.size() > 1:
for i in range(num):
simqueue.enqueue(simqueue.dequeue())
simqueue.dequeue()
return simqueue.dequeue()
print(hotPotato(["Bill","David","Susan","Jane","Kent","Brad"],7))
```
## Lab3 - 實作 Linked List
:::info
參考: [3.21.实现无序列表:链表](https://facert.gitbooks.io/python-data-structure-cn/3.%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/3.21.%E5%AE%9E%E7%8E%B0%E6%97%A0%E5%BA%8F%E5%88%97%E8%A1%A8%EF%BC%9A%E9%93%BE%E8%A1%A8/)
:::
* Unordered List v.s. Ordered List
* Unordered List: 無序列表,元素按照被放進去的順序排列
* Ordered List: 有序列表,元素照自身大小排列
這邊實作的是Unordered List
* 什麼是Linked List(鏈結串列)
這就是Linked List,資料存在節點上面,每個節點接到下個節點,開頭是一個head,指向第一個節點的位置

Node包含了兩個屬性:
* data: 自己的資料
* next: 下一個節點的位置
Node包含這幾個function:
* getData(): 回傳
* getNext(): 回傳下一個人的位置
* setData(newData): 設定自己的資料
* setNext(newNext): 設定下個節點的位置
而整個Linked List包含一個屬性:
* head: 指向第一個Node的位置
有以下幾個function:
* isEmpty(): 回傳裡面是不是空的
* add(item): 將項目新增到最前面

* remove(item): 將項目刪除
* size(): 回傳有幾個項目
* append(item): 將項目新增到最尾端
* index(item): 回傳項目的索引值
* insert(pos, item): 將項目插入特定位置
* pop(): 刪除並回傳最後一個項目
* pop(item): 刪除並回傳特定位置項目
* 程式碼
Node
```python=
class Node:
def __init__(self, initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self, newdata):
self.data = newdata
def setNext(self, newnext):
self.next = newnext
```
Unordered List
```python=
class UnorderedList:
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def add(self, item):
temp = Node(item)
temp.setNext(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.getNext()
return count
def search(self, item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
def append(self, item):
pass
def index(self, item):
pass
def insert(self, item):
pass
def pop(self, pos=self.size()-1):
pass
```
## Homework1 - 實作 Stack
:::info
參考: [3.5.Python实现栈](https://facert.gitbooks.io/python-data-structure-cn/3.%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/3.5.Python%E5%AE%9E%E7%8E%B0%E6%A0%88/)
:::
* Stack要有以下function:
* push(item): 將項目放入stack頂端
* pop(): 從頂端刪除並回傳項目
* peek(): 從頂端回傳項目,不刪除
* isEmpty(): 回傳stack是否為空的
* size(): 回傳stack項目數量
## Homework2 - 利用Stack實作中序式轉後序式
:::info
參考: [3.9.中缀,前缀和后缀表达式](https://facert.gitbooks.io/python-data-structure-cn/3.%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/3.9.%E4%B8%AD%E7%BC%80%E5%89%8D%E7%BC%80%E5%92%8C%E5%90%8E%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F/)
:::
* 中序式: ( 3 + 2 ) * 4 - 1
* 後序式: 3 2 + 4 * 1 -
* 前序式: - * + 3 2 4 1
寫一個程式將中序式轉換為後序式