溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python的代理類怎么實現(xiàn)

發(fā)布時間:2022-03-22 09:07:59 來源:億速云 閱讀:154 作者:iii 欄目:開發(fā)技術(shù)

這篇“Python的代理類怎么實現(xiàn)”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Python的代理類怎么實現(xiàn)”文章吧。

代理類的一個簡單的實現(xiàn)方式示例

目標(biāo):實現(xiàn)類Product的實例屬性讓另一個類Proxy來代理訪問和控制,想將對外公布的屬性交給代理類讓外部訪問和控制,不想對外公布的屬性無法通過代理來訪問和控制,這里不想對外公布的屬性約定用下劃線命名開頭

# proxy_example1.py
# 以下是一個代理類實現(xiàn)只讀訪問的示例
# 目標(biāo):代理后只能訪問和修改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):    # 本實例沒有找到的屬性會執(zhí)行__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,這會導(dǎo)致Proxy的實例都無法創(chuàng)建(__dict__等屬性無法創(chuàng)建)
            super(Proxy, self).__setattr__(key, value)   # 避免無限循環(huán)
        else:
            setattr(self._obj, key, value)
    # 要求只能刪除非下劃線開頭的屬性
    def __delattr__(self, item):
        if item.startswith("_"):
            super(Proxy, self).__delattr__(item)    # 避免無限循環(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è)置給了代理類
    print(p._curr)  # raise an error, 被代理的類Product的屬性沒有設(shè)置成功也無法訪問

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
    # 先創(chuàng)建在刪除
    pp._def = 123   # 這個操作只會設(shè)置Proxy的實例屬性
    print(pp._def)      # 訪問的是Proxy實例屬性,被代理的Product實例沒有創(chuàng)建_def屬性
    # del pp._def     # 刪除的是Proxy的實例屬性
    # print(pp._def)

測試獲取屬性

if __name__ == '__main__':
    test_getattr()

輸出:

10
...
Exception: _curr not found
...

測試設(shè)置屬性

if __name__ == '__main__':
    test_delattr()

輸出

1 1
10000
...
AttributeError: 'Product' object has no attribute '_curr'
...

測試刪除屬性

if __name__ == '__main__':    test_delattr()

輸出

123 123
123

注:以雙下劃線開頭和結(jié)尾的方法無法被代理,想要使用,必須在代理類中定義出這個方法,然后重定向到被代理的類的方法,比如你想使用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):    # 本實例沒有找到的屬性會執(zhí)行__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,這會導(dǎo)致Proxy的實例都無法創(chuàng)建(__dict__等屬性無法創(chuàng)建)
            super(Proxy, self).__setattr__(key, value)   # 避免無限循環(huán)
        else:
            setattr(self._obj, key, value)
    # 要求只能刪除非下劃線開頭的屬性
    def __delattr__(self, item):
        if item.startswith("_"):
            super(Proxy, self).__delattr__(item)    # 避免無限循環(huán)
        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

一個實現(xiàn)日志輸出的代理類的簡化示例

捕獲web server報錯日志并執(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類實例沒獲取到的屬性會執(zhí)行這個方法
        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.

以上就是關(guān)于“Python的代理類怎么實現(xiàn)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI