Python的代理類(lèi)實(shí)現(xiàn),控制訪問(wèn)和修改屬性的權(quán)限你都了解嗎
本篇文章主要內(nèi)容
代理類(lèi)主要功能是將一個(gè)類(lèi)實(shí)例的屬性訪問(wèn)和控制代理到代碼內(nèi)部另外一個(gè)實(shí)例類(lèi),將想對(duì)外公布的屬性的訪問(wèn)和控制權(quán)交給代理類(lèi)來(lái)操作,保留不想對(duì)外公布的屬性的訪問(wèn)或控制權(quán),比如只讀訪問(wèn),日志功能
1.代理類(lèi)實(shí)現(xiàn)被代理類(lèi)的屬性訪問(wèn)和修改權(quán)限控制
2.異常捕獲代理類(lèi)的簡(jiǎn)化示例
代理類(lèi)的一個(gè)簡(jiǎn)單的實(shí)現(xiàn)方式示例
目標(biāo):實(shí)現(xiàn)類(lèi)Product的實(shí)例屬性讓另一個(gè)類(lèi)Proxy來(lái)代理訪問(wèn)和控制,想將對(duì)外公布的屬性交給代理類(lèi)讓外部訪問(wèn)和控制,不想對(duì)外公布的屬性無(wú)法通過(guò)代理來(lái)訪問(wèn)和控制,這里不想對(duì)外公布的屬性約定用下劃線命名開(kāi)頭
# proxy_example1.py
# 以下是一個(gè)代理類(lèi)實(shí)現(xiàn)只讀訪問(wèn)的示例
# 目標(biāo):代理后只能訪問(wèn)和修改Product的公開(kāi)屬性,私有屬性_current只能查看不能修改
class Product:
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
self._current = 123
# 只暴露代理類(lèi)Proxy給外部使用
class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item): # 本實(shí)例沒(méi)有找到的屬性會(huì)執(zhí)行__getattr__方法
if item.startswith("_"): # 約定下劃線開(kāi)頭的方法不能訪問(wèn)到被代理的類(lèi),只會(huì)訪問(wèn)到代理類(lèi)
raise Exception(f"{item} not found") # Product存在的私有屬性也不希望被外部知道
return getattr(self._obj, item)
def __setattr__(self, key, value):
if key.startswith("_"): # 約定下劃線開(kāi)頭的方法不能訪問(wèn)到被代理的類(lèi),只會(huì)訪問(wèn)到代理類(lèi)
# 注:這里不能raise,這會(huì)導(dǎo)致Proxy的實(shí)例都無(wú)法創(chuàng)建(__dict__等屬性無(wú)法創(chuàng)建)
super(Proxy, self).__setattr__(key, value) # 避免無(wú)限循環(huán)
else:
setattr(self._obj, key, value)
# 要求只能刪除非下劃線開(kāi)頭的屬性
def __delattr__(self, item):
if item.startswith("_"):
super(Proxy, self).__delattr__(item) # 避免無(wú)限循環(huán)
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) # 私有屬性,設(shè)置給了代理類(lèi)
print(p._curr) # raise an error, 被代理的類(lèi)Product的屬性沒(méi)有設(shè)置成功也無(wú)法訪問(wèn)
def test_delattr():
p = Product(10, 2)
pp = Proxy(p)
pp.abc = 123
print(pp.abc, p.abc)
# 刪除公開(kāi)屬性
del pp.abc # 成功
# print(pp.abc, p.abc) # 已被刪除
# # 刪除私有屬性
# del pp._curr # 會(huì)嘗試刪除Proxy的私有屬性,raise AttributeError: _curr
# 先創(chuàng)建在刪除
pp._def = 123 # 這個(gè)操作只會(huì)設(shè)置Proxy的實(shí)例屬性
print(pp._def) # 訪問(wèn)的是Proxy實(shí)例屬性,被代理的Product實(shí)例沒(méi)有創(chuàng)建_def屬性
# del pp._def # 刪除的是Proxy的實(shí)例屬性
# print(pp._def)
測(cè)試獲取屬性
if __name__ == '__main__':
test_getattr()
輸出:
10
...
Exception: _curr not found
...
測(cè)試設(shè)置屬性
if __name__ == '__main__':
test_delattr()
輸出
1 1
10000
...
AttributeError: 'Product' object has no attribute '_curr'
...
測(cè)試刪除屬性
if __name__ == '__main__': test_delattr()
輸出
123 123
123
注:以雙下劃線開(kāi)頭和結(jié)尾的方法無(wú)法被代理,想要使用,必須在代理類(lèi)中定義出這個(gè)方法,然后重定向到被代理的類(lèi)的方法,比如你想使用isinstance()方法就要在Proxy偽造定義__class__屬性,想要使用len()方法就要在Proxy重定向返回到被代理的類(lèi)的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
# 只暴露代理類(lèi)Proxy給外部使用
class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, item): # 本實(shí)例沒(méi)有找到的屬性會(huì)執(zhí)行__getattr__方法
if item.startswith("_"): # 約定下劃線開(kāi)頭的方法不能訪問(wèn)到被代理的類(lèi),只會(huì)訪問(wèn)到代理類(lèi)
raise Exception(f"{item} not found") # Product存在的私有屬性也不希望被外部知道
return getattr(self._obj, item)
def __setattr__(self, key, value):
if key.startswith("_"): # 約定下劃線開(kāi)頭的方法不能訪問(wèn)到被代理的類(lèi),只會(huì)訪問(wèn)到代理類(lèi)
# 注:這里不能raise,這會(huì)導(dǎo)致Proxy的實(shí)例都無(wú)法創(chuàng)建(__dict__等屬性無(wú)法創(chuàng)建)
super(Proxy, self).__setattr__(key, value) # 避免無(wú)限循環(huán)
else:
setattr(self._obj, key, value)
# 要求只能刪除非下劃線開(kāi)頭的屬性
def __delattr__(self, item):
if item.startswith("_"):
super(Proxy, self).__delattr__(item) # 避免無(wú)限循環(huán)
else:
delattr(self._obj, item)
@property
def __class__(self): # 偽造類(lèi)
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__,會(huì)返回False
def test_len():
p = Product(10, 2)
pp = Proxy(p)
print(len(pp)) # 如果Proxy實(shí)例不定義__len__方法,會(huì)報(bào)錯(cuò)TypeError: object of type 'Proxy' has no len()
測(cè)試偽造的實(shí)例class類(lèi)型
if __name__ == '__main__':
test_instance()
輸出
<class '__main__.Product'>
True
測(cè)試獲取長(zhǎng)度
if __name__ == '__main__':
test_len()
輸出
111
一個(gè)實(shí)現(xiàn)日志輸出的代理類(lèi)的簡(jiǎn)化示例
捕獲web server報(bào)錯(cuò)日志并執(zhí)行異常處理的示例
# 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類(lèi)實(shí)例沒(méi)獲取到的屬性會(huì)執(zhí)行這個(gè)方法
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())
運(yùn)行輸出
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.
總結(jié)
本節(jié)主要的內(nèi)容是實(shí)現(xiàn)了一個(gè)代理類(lèi),達(dá)到代理訪問(wèn)和控制某個(gè)類(lèi)的屬性并避免將私有屬性暴露給外部,需要注意的是,一些特殊方法,也就是python雙下劃線開(kāi)頭和結(jié)尾的方法,如果想要被代理類(lèi)訪問(wèn)和控制,就必須在代理類(lèi)中也定義對(duì)應(yīng)的實(shí)際方法,另外,示例中主要是以下劃線開(kāi)頭的方法作為私有屬性的約定,也可以使用其他約定,這樣在代理方法中的訪問(wèn)和修改時(shí)做出相應(yīng)的判斷即可
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Python寫(xiě)入MySQL數(shù)據(jù)庫(kù)的三種方式詳解
Python 讀取數(shù)據(jù)自動(dòng)寫(xiě)入 MySQL 數(shù)據(jù)庫(kù),這個(gè)需求在工作中是非常普遍的,主要涉及到 python 操作數(shù)據(jù)庫(kù),讀寫(xiě)更新等。本文總結(jié)了Python寫(xiě)入MySQL數(shù)據(jù)庫(kù)的三種方式,需要的可以參考一下2022-06-06
Python用selenium實(shí)現(xiàn)自動(dòng)登錄和下單的項(xiàng)目實(shí)戰(zhàn)
本文主要介紹了Python用selenium實(shí)現(xiàn)自動(dòng)登錄和下單的項(xiàng)目實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
Python采集代理ip并判斷是否可用和定時(shí)更新的方法
今天小編就為大家分享一篇Python采集代理ip并判斷是否可用和定時(shí)更新的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Python實(shí)現(xiàn)定制自動(dòng)化業(yè)務(wù)流量報(bào)表周報(bào)功能【XlsxWriter模塊】
這篇文章主要介紹了Python實(shí)現(xiàn)定制自動(dòng)化業(yè)務(wù)流量報(bào)表周報(bào)功能,結(jié)合實(shí)例形式分析了Python基于XlsxWriter模塊操作xlsx文件生成報(bào)表圖的相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
python opencv 二值化 計(jì)算白色像素點(diǎn)的實(shí)例
今天小編就為大家分享一篇python opencv 二值化 計(jì)算白色像素點(diǎn)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07
windows系統(tǒng)上通過(guò)whl文件安裝triton模塊的簡(jiǎn)單步驟
這篇文章主要介紹了在Windows系統(tǒng)中通過(guò).whl文件安裝Triton的步驟,包括確認(rèn)系統(tǒng)環(huán)境、下載合適的.whl文件、使用pip安裝、驗(yàn)證安裝、使用Triton以及解決潛在問(wèn)題,需要的朋友可以參考下2025-01-01

