文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python的代理类怎么实现

2023-06-29 14:36

关注

这篇“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.pyclass 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__,会返回Falsedef 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 wrapsclass 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 attrif __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的代理类怎么实现”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯