iT邦幫忙

2021 iThome 鐵人賽

DAY 12
0
自我挑戰組

Python資料分析學習地圖系列 第 12

Day 12 : 物件導向

在進入機器學習之前,想先大家深入了解一些 python 的進階操作。接下來的操作會有點抽象,請好好品嚐/images/emoticon/emoticon12.gif

回到主題,什麼是物件導向程式設計? 大家或多或少打開別人寫的程式碼會看到 class (類別),寫成這個樣式有什麼優點嗎? 以下列舉三項:

  • 提供軟體的重複利用性
  • 擴充性
  • 方便維護

我們接著會將想實作的概念抽象化,方便大家使用。python 的套件基本上都是一個個物件哦!前面介紹的 pandas、matplotlib、seaborn,原始碼都是寫成物件導向。

OS:若想要往更高階的 python 技巧,物件導向可是非常重要一定要會的!

1. 類別的定義與使用

定義一個物件有的屬性(attribute)和方法(method),舉例定義一個動物,他的名稱和顏色是屬性,而動作是方法。Animal 是一個類別名稱,在類別內定義的變數稱為屬性、而定義的函式為方法。提醒一下這邊開始會看到很多 self 的關鍵字,這個代表自己的意思(想像成英文的代名詞),樓下這個 self 就是 animal。

你會在每個 method 最前面的第一個參數都必定寫 self

class Animal:
    
    # 定義屬性 (Attribut)
    name = '小黑'
    color = '黑色'
    
    # 定義方法 (Method)
    def walk(self):
        return '走路'

若想操作類別的屬性和方法,必須先宣告實體化該物件

  • object.屬性: 物件裡定義好的屬性,舉例汽車是一個物件,顏色就是它的屬性。
  • object.方法(): 物件裡定義好的方法(就是function),舉例汽車是一個物件,開車就是它的方法。
  • 實體化:基本上是把物件指向某個變數,例如汽車是一個物件,所以我寫 x = 汽車()
# 先實體化後指定給該變數,而該變數則為物件
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 : 走路

2. 類別的建構元

在建立物件的同時,程式會自動執行的方法稱為建構元,又稱為初始化。

這個通常是我們寫物件的「起手式」。

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)
>>> 小黑

小小提醒

  • self.radius 是物件的屬性
  • radius 是 __init__ 的區域變數
class Circle:
    
    def __init__(self):
        self.radius = 10
        radius = 5
c = Circle()
c.radius
>>> 10

3. 類別變數

  • (1) 可以直接存取該類別變數,而該類別變數與 Car 類別的任何物件無關
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.
  • (2) 如果用類別名稱來存取變數會有缺點,當有一天這個類別 Car 改成其他名稱就會出錯
    以下可以改成用 __class__ 的方式取代 Car.wheel_num
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.
  • (3) 建立物件變數並沒有修改到類別變數
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

4. 私有屬性與方法

剛剛介紹的類別,其類別內的屬性與方法都可以讓外部引用,稱為公有屬性或公有方法

如果我們想要隱密一點,創建私有的屬性和方法,則稱為「封裝」(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.

小小提醒

除了雙底線開頭的私有變數之外,單底線開頭(不以底線結尾)的變數也算是私有變數

這是屬於約定俗成的寫法,依舊可以存取

雖然可以存取,但別人設定寫私有就不要隨意取用

其他資源參考

5. 類別的繼承

我們想定義動物的物件,目前我家只養了兩隻動物就要寫兩個物件

但一旦到了動物園,有很多動物,每個動物叫聲(方法)都不太一樣,那不就要寫很多次嗎?

我們是不是可以用抽象的方式來定義呢?

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'

上一篇
Day 11 : psycopg2 操作
下一篇
Day 13 : 程式除錯與異常
系列文
Python資料分析學習地圖30

尚未有邦友留言

立即登入留言