- YT物件導向教學:
- https://www.youtube.com/watch?v=WBrX9n0SWG8
- https://www.youtube.com/watch?v=fELYoevwnag&t=273s
- [IThome文章](https://ithelp.ithome.com.tw/articles/10265795)
- 物件導向程式設計(Object-Oriented Programming, OOP)是一種程式設計範式,它將程式組織成物件,這些物件包含資料(屬性)和操作資料的行為(方法)。OOP 使得程式更具結構性、可讀性和可維護性。
```python=
class Robot:
# 初始化class(self, 其他自己接)
def __init__(self, name, age):
self.name = name
self.age = age
# 使用class自己的參數
def walk(self):
print(f"{self.name} is walking.")
# 除了class裡面的參數,還有額外參數
def sleep(self, hours):
print(f"{self.name} is going to sleep for {hours} hours.")
# 訪問屬性和方法
my_robot = Robot("助教",23)
print(type(my_robot)) # <class '__main__.Robot'>
print(my_robot.name) # 助教
print(my_robot.age) # 23
my_robot.walk() # 助教 is walking.
my_robot.sleep(15) # 助教 is going to sleep for 15 hours.
```
## oop class attribute(屬性)
- 屬性就想成,是HP、MP這種自身的參數
- 這邊要介紹另一部動漫<<[怪獸8號](https://www.youtube.com/watch?v=CarsLstLs2s)>>,對他很好看ww

- 那它裡面要的平凡人,如果要打怪獸,就要穿上解放衣,並且激發解放力

### 第一種寫法:每個對象都有自己的屬性
- 在這種寫法中,每個對象都有自己的 `name`、`age` 和 `ingredient` 屬性。
```python
class Robot1:
def __init__(self, name, age):
self.name = name
self.age = age
self.ingredient = "metal"
```
### 第二種寫法:使用類屬性
- 在這種寫法中,屬性 `ingredient` 被定義為類屬性,這意味著所有對象共享同一份 `ingredient` 屬性,而不是每個對象都有自己的副本。
- 這種寫法節省了空間,但如果需要為不同的對象設置不同的屬性值,就不適用。
```python
class Robot:
ingredient = "metal"
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"hi, my name is {self.name}. and i am made of {Robot.ingredient}.")
# 創建class
robot1 = Robot("weiwei", 25)
robot2 = Robot("robot2", 30)
robot1.greet()
# 訪問class
print("Robot1 ingredient:", robot1.ingredient) # 输出 "metal"
print("Robot2 ingredient:", robot2.ingredient) # 输出 "metal"
# 也可以通过class本身来訪問屬性
print("Class attribute ingredient:", Robot.ingredient) # 输出 "metal"
```
### 第三種寫法:使用 `self.__class__.attribute`
- 這種寫法類似於第二種寫法,但是在方法中使用了 `self.__class__.ingredient` 來訪問類屬性。這樣做的好處是,如果後續需要修改類屬性的值,只需在類定義中修改一次即可,所有對象都會自動更新,而無需修改每個對象的屬性值。
```python
class Robot:
ingredient = "metal"
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"hi, my name is {self.name}. and i am made of {self.__class__.ingredient}.")
# 創建class
robot1 = Robot("weiwei", 25)
robot2 = Robot("robot2", 30)
robot1.greet()
# 访问class
print("Robot1 ingredient:", robot1.ingredient) # 输出 "metal"
print("Robot2 ingredient:", robot2.ingredient) # 输出 "metal"
```
## 建構子與解構子
```python
class Person:
# 建構子
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"Hi, my name is {self.name} and I am {self.age} years old")
person = Person("助教", 23)
person.introduce() # Hi, my name is 助教 and I am 23 years old
```
- 解構子是 `__del__` 方法,用於在物件被銷毀前執行清理操作。
```python
# 解構子
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print(f"{self.name} is being deleted")
person = Person("助教", 23)
del person # 助教 is being deleted
```
## static method & class method
### Instance Method
- 第一個參數為 self。
- 實例化後,可以訪問物件屬性和實例方法。
```python
class MyClass:
def instance_method(self):
print("This is an instance method.")
obj = MyClass()
obj.instance_method() # This is an instance method.
```
### Class Method 類別方法
- 使用 @classmethod 裝飾器定義的方法,第一個參數為 cls。
- 整個類別相關的操作,可以訪問類別的屬性和其他類別方法。
```python
class MyClass:
class_variable = "Class Variable"
@classmethod
def class_method(cls):
print(f"Class Method. Class Variable: {cls.class_variable}")
# 使用類別方法
MyClass.class_method() # Class Method. Class Variable: Class Variable
```
### Static Method 靜態方法
- 使用 @staticmethod 裝飾器定義的方法,不需要特定的第一個參數。
- 與類別和實例無關的操作,不訪問實例和類別的屬性。
```python
class MyClass:
class_variable = "Class Variable"
@staticmethod
def static_method():
print("Static Method.")
# 使用靜態方法
MyClass.static_method() # Static Method.
```
### 綜合示範
```python
class Circle:
pi = 3.14159
all_circles = []
# 建構子
def __init__(self,radius):
self.radius = radius
self.all_circles.append(self)
# Instance Method(最一般的)
def area(self):
return self.pi * (self.radius**2)
# Class Method 類別方法
@classmethod
def total_area2(cls):
total = 0
for i in cls.all_circles:
total += i.area()
return total
# Static Method 靜態方法
@staticmethod
def total_area():
total = 0
for i in Circle.all_circles:
total += i.area()
return total
c1 = Circle(10)
print(c1.all_circles) # [<__main__.Circle object at 0x00000102F199B490>]
c2 = Circle(15)
print(c1.all_circles) # [<__main__.Circle object at 0x00000102F19797D0>, <__main__.Circle object at 0x00000102F0B6E490>]
print(c1.area()) # 314.159
print(c1.total_area2()) # 1021.01675
print(c1.total_area()) # 1021.01675
```
## 繼承(inheritance)
- 繼承允許一個類(子類)繼承另一個類(父類)的屬性和方法。
```python
class People:
def __init__(self, name, age) -> None:
self.name = name
self.age = age
def sleep(self):
print(f"{self.name} is sleeping...")
def eat(self):
print(f"{self.name} is eating...")
class Student(People):
def __init__(self, name, age, student_id) -> None:
super().__init__(name, age) # 繼承
self.student_id = student_id
def eat(self,food): # 方法可以被覆寫
print(f"{self.name} is eating now {food}...")
student1 = Student("助教", 23, 10911221)
print(student1.age, student1.student_id) # 23 10911221
student1.sleep() # 助教 is sleeping...
student1.eat("怪獸8號蟲蟲") # 助教 is eating now 怪獸8號蟲蟲...
```
## 多型(Polymorphism)
- 多型允許同一方法在不同物件中有不同的實現。
- 繼承時會覆寫方法
```python
class People:
def speak(self):
return "say something"
class 卡夫卡(People):
def speak(self):
return "我還可以以你的身邊為目標前進嗎?"
class 米娜(People):
def speak(self):
return "恩,我會一直等你的"
people1 = 卡夫卡()
people2 = 米娜()
print(people1.speak()) # '我還可以以你的身邊為目標前進嗎?'
print(people2.speak()) # '恩,我會一直等你的'
```
```python
class 亞白米娜(): # 防衛隊第3部隊的隊長
def __init__(self):
self.power = 96
self.years = 27
class 四之宮琪歌露(亞白米娜):
def __init__(self):
super().__init__() # 使用 super() 繼承 亞白米娜 __init__ 裡所有屬性
self.power = 55 # 如果屬性相同,則覆寫屬性
self.years = 16
self.sex = "female"
class 日比野卡夫卡(亞白米娜):
def __init__(self):
super().__init__() # 使用 super() 繼承 亞白米娜 __init__ 裡所有屬性
self.power = 1 # 如果屬性相同,則覆寫屬性
self.years = 32
self.sex = "male"
person = 四之宮琪歌露()
print(person.power) # 55
```
## 多重繼承
- 繼承不僅能進行單一繼承,也可以進行多重繼承,例如可以從爸爸身上繼承基因,同時也可以從媽媽身上繼承基因一般
```python
class father():
def __init__(self):
self.eye = 2
self.ear = 2
self.nose = 1
self.mouth = 1
class mother(): # mother 类
def language(self): # mother 的方法
print('english')
def skill(self):
print('painting')
class son(father, mother): # 继承 father 和 mother
def __init__(self):
super().__init__() # 繼承爸爸媽媽
def play(self):
print('play phone')
person = son()
print(person.eye) # 输出 2
person.skill() # 输出 painting
person.play() # 输出 ball
```
## 多層繼承
```python
class grandpa():
def __init__(self):
self.eye = 2
self.ear = 2
self.nose = 1
self.mouth = 1
class father(grandpa):
def __init__(self):
super().__init__() # 调用父类的构造函数
def language(self):
print('english')
def skill(self):
print('painting')
class son(father):
def __init__(self):
super().__init__() # 调用父类的构造函数
def play(self):
print('ball')
person = son()
print(person.eye) # 输出 2
person.skill() # 输出 painting
person.play() # 输出 ball
```
## 抽象(Abstraction)
- 抽象是指僅顯示物件的必要屬性和行為,隱藏內部的實現細節。
- python要借助套件實現
```python
from abc import ABC, abstractmethod
class Moster(ABC):
@abstractmethod
def say(self):
return "我會一直等你的"
class 卡夫卡(Moster):
def say(self):
return "我還可以以你的身邊為目標前進嗎?"
class 米娜(Moster):
def say(self):
return "恩,我會一直等你的"
person1 = 卡夫卡()
person2 = 米娜()
print(person1.say())# 我還可以以你的身邊為目標前進嗎?
print(person2.say())# 恩,我會一直等你的
# error
thief = Moster()
thief.say() # Can't instantiate abstract class Moster with abstract method say
```
## 封裝(Encapsulation)與私有化
- 封裝是將資料和方法綁定在一起,並隱藏物件的內部細節。可以透過在屬性或方法前加上雙下劃線(__)將其設為私有。
### getter & setter
```python
class Robot:
def __init__(self,name, age) -> None:
self.name = name
self.age = age
self.sex = "male" # public
my_robot = Robot("TA", 23)
my_robot.sex = "female" # 數值被隨意篡改了,也正因此有了private的概念
print(my_robot.sex) # female
```
```python
# 增加getter跟setter的概念
class Robot:
def __init__(self,name, age) -> None:
self.name = name
self.age = age
self.__sex = "male"
# getter
def getter(self):
return self.__sex
# setter
def setter(self, new_sex): # 更改要透過函數
self.__sex = new_sex
my_robot = Robot("TA", 23)
my_robot.getter() # male
my_robot.setter("female")
my_robot.getter() # female
```
## 裝飾器@property decorator(只讀)
- 如果在類別裡有些屬性不希望被外部更動,就能夠使用 @property 的裝飾器,將該屬性設為唯讀屬性
- 要使用的話,要用屬性的方式歐
```python
class 卡夫卡():
def __init__(self):
self.sentence = "我還可以以你的身邊為目標前進嗎?"
def say(self):
return self.sentence
person1 = 卡夫卡()
person1.say() # '我還可以以你的身邊為目標前進嗎?'
person1.sentence = "我是金句破壞王" # 屬性被更改掉了
person1.say() # '我是金句破壞王'
```
```python
# 將sentence設為only read
class 米娜():
def __init__(self):
self.sentence = "恩,我會一直等你的"
@property
def say(self):
return self.sentence
person1 = 米娜()
person1.say # '恩,我會一直等你的'
# person1.say = "4546" # error
```
## Lab08/作業題目
- 繳交方式 : 請到[https://140.116.179.59:8080](https://140.116.179.59:8080)完成作業題目,並將程式碼加上註解(你的理解),很重要,否則助教有權利扣你分數。
- 禁止抄襲,否則助教會來查水表。