在進入機器學習之前,想先大家深入了解一些 python 的進階操作。接下來的操作會有點抽象,請好好品嚐
回到主題,什麼是物件導向程式設計? 大家或多或少打開別人寫的程式碼會看到 class (類別),寫成這個樣式有什麼優點嗎? 以下列舉三項:
我們接著會將想實作的概念抽象化,方便大家使用。python 的套件基本上都是一個個物件哦!前面介紹的 pandas、matplotlib、seaborn,原始碼都是寫成物件導向。
OS:若想要往更高階的 python 技巧,物件導向可是非常重要一定要會的!
定義一個物件有的屬性(attribute)和方法(method),舉例定義一個動物,他的名稱和顏色是屬性,而動作是方法。Animal 是一個類別名稱,在類別內定義的變數稱為屬性、而定義的函式為方法。提醒一下這邊開始會看到很多 self 的關鍵字,這個代表自己的意思(想像成英文的代名詞),樓下這個 self 就是 animal。
你會在每個 method 最前面的第一個參數都必定寫 self
class Animal:
# 定義屬性 (Attribut)
name = '小黑'
color = '黑色'
# 定義方法 (Method)
def walk(self):
return '走路'
若想操作類別的屬性和方法,必須先宣告實體化該物件
# 先實體化後指定給該變數,而該變數則為物件
a = Animal()
print("Type :", type(a))
print("Attribute 1 :", a.name)
print("Attribute 2 :", a.color)
print("Method :", a.walk())
>>> Type : <class '__main__.Animal'>
Attribute 1 : 小黑
Attribute 2 : 黑色
Method : 走路
在建立物件的同時,程式會自動執行的方法稱為建構元,又稱為初始化。
這個通常是我們寫物件的「起手式」。
class Animal:
def __init__(self, name, color):
print("Initialize")
self.name = name
self.color = color
def call(self):
print(f"The color of my {self.name} is {self.color}")
# 實體化的時候就會進行初始化的動作
a = Animal('小黑', '黑色')
>>> Initialize
a.call()
>>> The color of my 小黑 is 黑色
print(a.name)
>>> 小黑
class Circle:
def __init__(self):
self.radius = 10
radius = 5
c = Circle()
c.radius
>>> 10
class Car:
wheel_num = 4
def __init__(self, brand):
self.brand = brand
def drive(self):
print(f"It has {Car.wheel_num} wheels.")
print(f"I can drive my {self.brand} car.")
Car.wheel_num
>>> 4
c = Car('Toyota')
c.drive()
>>> It has 4 wheels.
I can drive my Toyota car.
class Tricycle:
wheel_num = 3
def __init__(self, brand):
self.brand = brand
def drive(self):
print(f"It has {Car.wheel_num} wheels.")
print(f"I can drive my {self.brand} car.")
c = Tricycle('Toyota')
c.drive()
>>> It has 4 wheels.
I can drive my Toyota car.
class Car:
wheel_num = 4
def __init__(self, brand):
self.brand = brand
def drive(self):
print(f"It has {self.__class__.wheel_num} wheels.")
print(f"I can drive my {self.brand} car.")
c = Car('Toyota')
c.drive()
>>> It has 4 wheels.
I can drive my Toyota car.
class Circle:
pi = 3.14
def __init__(self, radius):
self.radius = radius
def calculate_area(self):
self.area = (radius**2) * self.__class__.pi
倘若我去實例化去修改它,c2.pi 、Circle.pi 沒有仍然被修改到
c1 = Circle(1)
c2 = Circle(2)
c1.pi = 3.14159
print("c1.pi:", c1.pi)
print("c2.pi:", c2.pi)
print("Circle.pi:", Circle.pi)
>>> c1.pi: 3.14159
c2.pi: 3.14
Circle.pi: 3.14
剛剛介紹的類別,其類別內的屬性與方法都可以讓外部引用,稱為公有屬性或公有方法
如果我們想要隱密一點,創建私有的屬性和方法,則稱為「封裝」(Encapsulation)。
class Bank:
def __init__(self, name):
self.name = name
self.balance = 0
def save_money(self, money):
self.balance += money
def withdraw_money(self, money):
self.balance -= money
def show_money(self):
print(f"{self.name} balance is NT${self.balance}.")
b = Bank("David")
b.save_money(1500)
b.show_money()
>>> David balance is NT$1500.
b.balance = 100000000
b.show_money()
>>> David balance is NT$100000000.
為了避免有心人士竄改數據,我們要求外部不得直接更改數據
class Bank:
def __init__(self, name):
self.name = name
self.__balance = 0
def save_money(self, money):
self.__balance += money
def withdraw_money(self, money):
self.__balance -= money
def show_money(self):
print(f"{self.name} balance is NT${self.__balance}.")
b = Bank("David")
b.balance = 100000000
b.show_money()
>>> David balance is NT$0.
私有屬性其實只是換上不同名稱(python將該屬性的名稱做調整),告訴其他開發者說這個不要串改或使用
b._Bank__balance = 10000000
b.show_money()
>>> David balance is NT$10000000.
除了雙底線開頭的私有變數之外,單底線開頭(不以底線結尾)的變數也算是私有變數
這是屬於約定俗成的寫法,依舊可以存取
雖然可以存取,但別人設定寫私有就不要隨意取用
我們想定義動物的物件,目前我家只養了兩隻動物就要寫兩個物件
但一旦到了動物園,有很多動物,每個動物叫聲(方法)都不太一樣,那不就要寫很多次嗎?
我們是不是可以用抽象的方式來定義呢?
class Dog:
def __init__(self, name):
self.name = name
def sleep(self):
print("I like sleeping.")
def bark(self):
print("bark bark bark")
class Cat:
def __init__(self, name):
self.name = name
def sleep(self):
print("I like sleeping.")
def meow(self):
print("meow meow meow")
上述的做法沒有不對,但是不夠好
改良一下把他們抽象化一點,把一樣的東西抽出來,加上繼承吧!
class Animals:
def __init__(self, name):
self.name = name
print(f"My name is {self.name}")
def talk(self):
pass
class Dog(Animals):
def __init__(self, dog_name):
super().__init__(dog_name)
def talk(self):
print("bark bark bark")
class Cat(Animals):
def __init__(self, cat_name):
super().__init__(cat_name)
def talk(self):
print("meow meow meow")
實際呼叫看看吧!
d = Dog('小黑')
d.talk()
>>> My name is 小黑
bark bark bark
c = Cat('小皮')
c.talk()
>>> My name is 小皮
meow meow meow
在物件導向的程式設計,類別是可以被繼承的哦!
被繼承的類別為父類別;繼承的類別為子類別
類別定義外的私有屬性或方法,子類別無法讀取,使用上請注意!
class Animals:
def __init__(self, name):
self.name = name
print(f"My name is {self.name}")
def talk(self):
pass
def author(self):
self.__title = 'Erik'
def author_title(self):
return self.__title
class Dog(Animals):
def __init__(self, dog_name):
super().__init__(dog_name)
def talk(self):
print("bark bark bark")
class Cat(Animals):
def __init__(self, cat_name):
super().__init__(cat_name)
def talk(self):
print("meow meow meow")
d = Dog('小黑')
>>> My name is 小黑
# 無法直接取得!
d.author()
d.__title()
>>> AttributeError: 'Dog' object has no attribute '__title'
# 我有給你方法,所以請透過我!
d.author_title()
>>> 'Erik'