Python - 包裝類別

介紹包裝類別

你好,未來的 Python 巫師們!今天,我們將進入包裝類別的精彩世界。如果你是編程新手,不必擔心——我會一步步引導你理解這個概念,就像過去多年來我對無數學生的教學一樣。

Python - Wrapper Classes

想像一下,你有一份美麗的禮物,但你想讓它更特別,於是就用花哨的紙張來包裝它。這就是我們在 Python 中使用包裝類別的方式——我們取得現有的物件並用額外的功能來「包裝」它們。很酷,對吧?

什麼是包裝類別?

包裝類別是一個圍繞(或「包裝」)另一個類別的物件或原始資料型態的類別。這就像在你的智慧型手機上加上保護殼——手機仍然正常工作,但現在它有了額外的功能和保護。

為什麼使用包裝類別?

  1. 向現有物件添加新功能
  2. 修改現有方法的行為
  3. 控制對原始物件的訪問

讓我們深入一些代碼示例,看看這如何在實際中工作!

基本包裝類別示例

class StringWrapper:
def __init__(self, string):
self.string = string

def get_string(self):
return self.string

def append(self, text):
self.string += text

# 使用我們的包裝
wrapped_string = StringWrapper("Hello")
print(wrapped_string.get_string())  # 輸出:Hello
wrapped_string.append(" World!")
print(wrapped_string.get_string())  # 輸出:Hello World!

在這個例子中,我們為字符串創建了一個簡單的包裝類別。讓我們來解析一下:

  1. 我們定義了一個名為 StringWrapper 的類別。
  2. __init__ 方法用於初始化我們的包裝與字符串。
  3. get_string() 讓我們能夠检索包裝的字符串。
  4. append() 是一個新增的方法,它為我們的字符串添加了功能——將文字附加到我們的字符串。

看我們是如何為基本的字符串添加了新功能(附加)?這就是包裝類別的力量!

使用包裝類別修改行為

現在,讓我們看看如何修改現有方法的行為:

class ShoutingList(list):
def __getitem__(self, index):
return super().__getitem__(index).upper()

# 使用我們的包裝
normal_list = ["hello", "world", "python"]
shouting_list = ShoutingList(normal_list)

print(normal_list[0])     # 輸出:hello
print(shouting_list[0])   # 輸出:HELLO

在這個例子中:

  1. 我們創建了一個繼承自內置 list 類別的 ShoutingList 類別。
  2. 我們覆寫了 __getitem__ 方法以返回大寫字符串。
  3. 當我們訪問 ShoutingList 中的項目時,它們會自動轉換為大寫。

這就像有一個朋友總是會在重複你說的話時大聲說出——內容一樣,但表達方式不同!

使用包裝類別控制訪問

包裝類別也可以用來控制對原始物件的訪問。這對數據保護或實現唯讀物件特別有用:

class ReadOnlyWrapper:
def __init__(self, data):
self._data = data

def get_data(self):
return self._data

def __setattr__(self, name, value):
if name == '_data':
super().__setattr__(name, value)
else:
raise AttributeError("This object is read-only")

# 使用我們的包裝
data = [1, 2, 3]
read_only_data = ReadOnlyWrapper(data)

print(read_only_data.get_data())  # 輸出:[1, 2, 3]
read_only_data.get_data().append(4)  # 這可以工作,修改了原始列表
print(read_only_data.get_data())  # 輸出:[1, 2, 3, 4]

try:
read_only_data.new_attribute = "Can't add this"
except AttributeError as e:
print(e)  # 輸出:This object is read-only

在這個例子中:

  1. 我們創建了一個 ReadOnlyWrapper 類別,只允許讀取數據。
  2. 我們覆寫了 __setattr__ 以防止向包裝添加新屬性。
  3. 原始數據仍然可以通過 get_data() 修改,但不能向包裝本身添加新的屬性。

這就像博物館的展覽——你可以看,但不能觸摸!

包裝類別的實際應用

包裝類別在現實世界中有許多應用。以下是一些例子:

  1. 日誌記錄:包裝物件以記錄方法調用或屬性訪問。
  2. 緩存:在昂貴的操作周圍實現緩存層。
  3. 輸入驗證:添加檢查以確保數據在使用前符合特定標準。
  4. 延遲加載:延遲創建物件,直到實際需要它們。

讓我們實現一個簡單的日誌包裝:

import time

class LoggingWrapper:
def __init__(self, obj):
self.wrapped_obj = obj

def __getattr__(self, name):
original_attr = getattr(self.wrapped_obj, name)
if callable(original_attr):
def wrapper(*args, **kwargs):
start_time = time.time()
result = original_attr(*args, **kwargs)
end_time = time.time()
print(f"Called {name}, took {end_time - start_time:.2f} seconds")
return result
return wrapper
return original_attr

# 使用我們的日誌包裝
class SlowCalculator:
def add(self, x, y):
time.sleep(1)  # 模擬一個慢操作
return x + y

calc = SlowCalculator()
logged_calc = LoggingWrapper(calc)

result = logged_calc.add(3, 4)
print(f"Result: {result}")

輸出:

Called add, took 1.00 seconds
Result: 7

在這個例子中:

  1. 我們創建了一個 LoggingWrapper 來包裝任何物件。
  2. 它攔截方法調用,記錄所用時間,然後調用原始方法。
  3. 我們使用它來包裝一個 SlowCalculator 物件並記錄其方法調用。

這就像有一個個人助手,他會記錄你所有的任務時間,然後向你匯報!

結論

包裝類別是 Python 中的一項強大工具,可以靈活地擴展、修改和控制物件。它們就像物件導向編程中的瑞士軍刀——多功能且在正確的情況下非常有用。

記住,掌握包裝類別的關鍵是實踐。嘗試為不同的物件創建自己的包裝,並看看如何增強它們的功能。誰知道呢?你可能就這樣包裝著成為 Python 高手!

編程愉快,願你的代碼永遠整齊包裝!??



| 方法 | 描述 |
|--------|-------------|
| `__init__(self, obj)` | 使用要包裝的物件初始化包裝 |
| `__getattr__(self, name)` | 攔截包裝上的屬性訪問 |
| `__setattr__(self, name, value)` | 攔截包裝上的屬性賦值 |
| `__getitem__(self, key)` | 攔截項目訪問(例如,對於類似列表的物件) |
| `__setitem__(self, key, value)` | 攔截項目賦值 |
| `__call__(self, *args, **kwargs)` | 如果包裝的物件可調用,則使包裝可調用 |
| `__iter__(self)` | 如果包裝的物件可迭代,則使包裝可迭代 |
| `__len__(self)` | 實現包裝的長度報告 |
| `__str__(self)` | 自定義包裝的字符串表示 |
| `__repr__(self)` | 自定義包裝的 repr 表示 |

這些方法讓你幾乎可以自定義包裝類別的每一個行為,為你提供對包裝物件與代碼其他部分交互的細粒度控制。

Credits: Image by storyset