Python 類別與物件 (Class & Object)
Python 是一個物件導向程式語言(Object-Oriented Programming, OOP)。類別是物件的藍圖,定義了物件的屬性和行為。
基本概念
- 類別 (Class):物件的模板或藍圖
- 物件 (Object):類別的實例 (instance)
- 屬性 (Attribute):物件的資料
- 方法 (Method):物件的行為(函數)
定義類別
使用 class 關鍵字定義類別:
class Dog:
pass # 空的類別
# 建立物件(實例化)
my_dog = Dog()
print(type(my_dog)) # <class '__main__.Dog'>
__init__ 方法
__init__ 是建構子,在建立物件時自動執行:
class Dog:
def __init__(self, name, age):
self.name = name # 實例屬性
self.age = age
# 建立物件
my_dog = Dog("Buddy", 3)
print(my_dog.name) # Buddy
print(my_dog.age) # 3
self 參數
self 代表物件本身,所有實例方法的第一個參數必須是 self:
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} says: Woof!")
def introduce(self):
print(f"I'm {self.name}")
dog = Dog("Buddy")
dog.bark() # Buddy says: Woof!
dog.introduce() # I'm Buddy
呼叫方法時不需要傳入
self,Python 會自動傳入。實例屬性 vs 類別屬性
class Dog:
# 類別屬性 - 所有實例共享
species = "Canis familiaris"
def __init__(self, name, age):
# 實例屬性 - 每個實例獨立
self.name = name
self.age = age
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
# 實例屬性
print(dog1.name) # Buddy
print(dog2.name) # Max
# 類別屬性
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
print(Dog.species) # Canis familiaris
修改類別屬性
class Counter:
count = 0
def __init__(self):
Counter.count += 1 # 透過類別名稱修改
c1 = Counter()
c2 = Counter()
c3 = Counter()
print(Counter.count) # 3
實例方法、類別方法、靜態方法
class MyClass:
class_attr = "I'm a class attribute"
def __init__(self, value):
self.instance_attr = value
# 實例方法 - 第一個參數是 self
def instance_method(self):
return f"Instance: {self.instance_attr}"
# 類別方法 - 第一個參數是 cls
@classmethod
def class_method(cls):
return f"Class: {cls.class_attr}"
# 靜態方法 - 沒有 self 或 cls
@staticmethod
def static_method():
return "Static method"
obj = MyClass("Hello")
# 實例方法需要透過物件呼叫
print(obj.instance_method()) # Instance: Hello
# 類別方法可以透過類別或物件呼叫
print(MyClass.class_method()) # Class: I'm a class attribute
print(obj.class_method()) # Class: I'm a class attribute
# 靜態方法可以透過類別或物件呼叫
print(MyClass.static_method()) # Static method
print(obj.static_method()) # Static method
類別方法的應用
類別方法常用於替代建構子:
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_string):
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
@classmethod
def today(cls):
import datetime
t = datetime.date.today()
return cls(t.year, t.month, t.day)
date1 = Date(2024, 12, 8)
date2 = Date.from_string("2024-12-08")
date3 = Date.today()
屬性存取控制
Python 沒有真正的 private,但有命名慣例:
class Person:
def __init__(self, name, age):
self.name = name # 公開屬性
self._email = None # 約定私有(單底線)
self.__password = None # 名稱改編(雙底線)
p = Person("Alice", 25)
# 公開屬性
print(p.name) # Alice
# 單底線 - 約定私有,但仍可存取
p._email = "alice@example.com"
print(p._email) # alice@example.com
# 雙底線 - 名稱改編,不建議直接存取
# print(p.__password) # AttributeError
print(p._Person__password) # 可以這樣存取,但不建議
Property 裝飾器
使用 @property 建立 getter 和 setter:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2
circle = Circle(5)
print(circle.radius) # 5
print(circle.area) # 78.53...
circle.radius = 10
print(circle.radius) # 10
# circle.radius = -1 # ValueError
# circle.area = 100 # AttributeError (沒有 setter)
完整範例
class BankAccount:
# 類別屬性
bank_name = "Python Bank"
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = balance
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"Deposited ${amount}. New balance: ${self._balance}")
else:
print("Deposit amount must be positive")
def withdraw(self, amount):
if amount > self._balance:
print("Insufficient funds")
elif amount <= 0:
print("Withdrawal amount must be positive")
else:
self._balance -= amount
print(f"Withdrew ${amount}. New balance: ${self._balance}")
def __str__(self):
return f"Account({self.owner}, ${self._balance})"
# 使用
account = BankAccount("Alice", 1000)
print(account) # Account(Alice, $1000)
account.deposit(500) # Deposited $500. New balance: $1500
account.withdraw(200) # Withdrew $200. New balance: $1300
print(account.balance) # 1300