Python - 包裝類別
介紹包裝類別
你好,未來的 Python 巫師們!今天,我們將進入包裝類別的精彩世界。如果你是編程新手,不必擔心——我會一步步引導你理解這個概念,就像過去多年來我對無數學生的教學一樣。
想像一下,你有一份美麗的禮物,但你想讓它更特別,於是就用花哨的紙張來包裝它。這就是我們在 Python 中使用包裝類別的方式——我們取得現有的物件並用額外的功能來「包裝」它們。很酷,對吧?
什麼是包裝類別?
包裝類別是一個圍繞(或「包裝」)另一個類別的物件或原始資料型態的類別。這就像在你的智慧型手機上加上保護殼——手機仍然正常工作,但現在它有了額外的功能和保護。
為什麼使用包裝類別?
- 向現有物件添加新功能
- 修改現有方法的行為
- 控制對原始物件的訪問
讓我們深入一些代碼示例,看看這如何在實際中工作!
基本包裝類別示例
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!
在這個例子中,我們為字符串創建了一個簡單的包裝類別。讓我們來解析一下:
- 我們定義了一個名為
StringWrapper
的類別。 -
__init__
方法用於初始化我們的包裝與字符串。 -
get_string()
讓我們能夠检索包裝的字符串。 -
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
在這個例子中:
- 我們創建了一個繼承自內置
list
類別的ShoutingList
類別。 - 我們覆寫了
__getitem__
方法以返回大寫字符串。 - 當我們訪問
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
在這個例子中:
- 我們創建了一個
ReadOnlyWrapper
類別,只允許讀取數據。 - 我們覆寫了
__setattr__
以防止向包裝添加新屬性。 - 原始數據仍然可以通過
get_data()
修改,但不能向包裝本身添加新的屬性。
這就像博物館的展覽——你可以看,但不能觸摸!
包裝類別的實際應用
包裝類別在現實世界中有許多應用。以下是一些例子:
- 日誌記錄:包裝物件以記錄方法調用或屬性訪問。
- 緩存:在昂貴的操作周圍實現緩存層。
- 輸入驗證:添加檢查以確保數據在使用前符合特定標準。
- 延遲加載:延遲創建物件,直到實際需要它們。
讓我們實現一個簡單的日誌包裝:
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
在這個例子中:
- 我們創建了一個
LoggingWrapper
來包裝任何物件。 - 它攔截方法調用,記錄所用時間,然後調用原始方法。
- 我們使用它來包裝一個
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