這篇“Python的代理類怎么實現”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Python的代理類怎么實現”文章吧。
目標:實現類Product的實例屬性讓另一個類Proxy來代理訪問和控制,想將對外公布的屬性交給代理類讓外部訪問和控制,不想對外公布的屬性無法通過代理來訪問和控制,這里不想對外公布的屬性約定用下劃線命名開頭
# proxy_example1.py
# 以下是一個代理類實現只讀訪問的示例
# 目標:代理后只能訪問和修改Product的公開屬性,私有屬性_current只能查看不能修改
class Product:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
self._current = 123
# 只暴露代理類Proxy給外部使用
class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item): # 本實例沒有找到的屬性會執行__getattr__方法
if item.startswith("_"): # 約定下劃線開頭的方法不能訪問到被代理的類,只會訪問到代理類
raise Exception(f"{item} not found") # Product存在的私有屬性也不希望被外部知道
return getattr(self._obj, item)
def __setattr__(self, key, value):
if key.startswith("_"): # 約定下劃線開頭的方法不能訪問到被代理的類,只會訪問到代理類
# 注:這里不能raise,這會導致Proxy的實例都無法創建(__dict__等屬性無法創建)
super(Proxy, self).__setattr__(key, value) # 避免無限循環
else:
setattr(self._obj, key, value)
# 要求只能刪除非下劃線開頭的屬性
def __delattr__(self, item):
if item.startswith("_"):
super(Proxy, self).__delattr__(item) # 避免無限循環
else:
delattr(self._obj, item)
def test_getattr():
p = Product(10, 1)
pp = Proxy(p)
print(pp.price)
print(pp._curr)
def test_setattr():
p = Product(10, 2)
pp = Proxy(p)
pp.abc = 1
print(pp.abc, p.abc)
pp._curr = 10000
print(pp._curr) # 私有屬性,設置給了代理類
print(p._curr) # raise an error, 被代理的類Product的屬性沒有設置成功也無法訪問
def test_delattr():
p = Product(10, 2)
pp = Proxy(p)
pp.abc = 123
print(pp.abc, p.abc)
# 刪除公開屬性
del pp.abc # 成功
# print(pp.abc, p.abc) # 已被刪除
# # 刪除私有屬性
# del pp._curr # 會嘗試刪除Proxy的私有屬性,raise AttributeError: _curr
# 先創建在刪除
pp._def = 123 # 這個操作只會設置Proxy的實例屬性
print(pp._def) # 訪問的是Proxy實例屬性,被代理的Product實例沒有創建_def屬性
# del pp._def # 刪除的是Proxy的實例屬性
# print(pp._def)測試獲取屬性
if __name__ == '__main__': test_getattr()
輸出:
10
...
Exception: _curr not found
...
測試設置屬性
if __name__ == '__main__': test_delattr()
輸出
1 1
10000
...
AttributeError: 'Product' object has no attribute '_curr'
...
測試刪除屬性
if __name__ == '__main__': test_delattr()
輸出
123 123
123
注:以雙下劃線開頭和結尾的方法無法被代理,想要使用,必須在代理類中定義出這個方法,然后重定向到被代理的類的方法,比如你想使用isinstance()方法就要在Proxy偽造定義__class__屬性,想要使用len()方法就要在Proxy重定向返回到被代理的類的len方法
# proxy_example2.py
class Product:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
self._current = 123
def __len__(self):
return 111
# 只暴露代理類Proxy給外部使用
class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item): # 本實例沒有找到的屬性會執行__getattr__方法
if item.startswith("_"): # 約定下劃線開頭的方法不能訪問到被代理的類,只會訪問到代理類
raise Exception(f"{item} not found") # Product存在的私有屬性也不希望被外部知道
return getattr(self._obj, item)
def __setattr__(self, key, value):
if key.startswith("_"): # 約定下劃線開頭的方法不能訪問到被代理的類,只會訪問到代理類
# 注:這里不能raise,這會導致Proxy的實例都無法創建(__dict__等屬性無法創建)
super(Proxy, self).__setattr__(key, value) # 避免無限循環
else:
setattr(self._obj, key, value)
# 要求只能刪除非下劃線開頭的屬性
def __delattr__(self, item):
if item.startswith("_"):
super(Proxy, self).__delattr__(item) # 避免無限循環
else:
delattr(self._obj, item)
@property
def __class__(self): # 偽造類
return self._obj.__class__
def __len__(self):
return len(self._obj)
def test_instance():
p = Product(10, 2)
pp = Proxy(p)
print(pp.__class__)
print(isinstance(pp, Product)) # 如果不偽造__class__,會返回False
def test_len():
p = Product(10, 2)
pp = Proxy(p)
print(len(pp)) # 如果Proxy實例不定義__len__方法,會報錯TypeError: object of type 'Proxy' has no len()測試偽造的實例class類型
if __name__ == '__main__': test_instance()
輸出
<class '__main__.Product'>
True
測試獲取長度
if __name__ == '__main__': test_len()
輸出
111
捕獲web server報錯日志并執行異常處理的示例
# logger_proxy.py
# -*- coding:utf-8 -*-
from functools import wraps
class DAL:
@classmethod
def dm1(cls, req, *args):
print("dm1...", f"{req=}")
print(1/0) # 故意拋出異常
return "dm1"
class BLL:
@classmethod
def bm1(cls, req):
print("bm1...", f"{req=}")
return DAL.dm1(req)
class Application:
def __init__(self, req):
self.req = req
self._p = "private attr"
def hd1(self):
return BLL.bm1(self.req)
class LoggerProxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item): # LoggerProxy類實例沒獲取到的屬性會執行這個方法
attr = getattr(self._obj, item)
if callable(attr): # 獲取到了方法,則處理異常捕獲
@wraps(attr)
def wrapped_method(*args, **kwargs):
# print(f"Before access to attribute/method: {item}")
try:
method = attr(*args, **kwargs)
except ZeroDivisionError:
# 捕獲異常然后處理...
raise Exception(f"{attr.__name__} received a zero division error.")
# print(f"After attribute/method {item} returned")
return method
return wrapped_method
else: # 獲取到了屬性,直接返回
return attr
if __name__ == '__main__':
lp = LoggerProxy(Application("abc"))
print(lp.req)
print(lp._p)
print(lp.hd1())運行輸出
abc
private attr
bm1... req='abc'
dm1... req='abc'
Traceback...
ZeroDivisionError: division by zero
During handling of the above exception, another exception occurred:
Traceback...
Exception: hd1 received a zero division error.
以上就是關于“Python的代理類怎么實現”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。