本篇文章主要内容

代理类主要功能是将一个类实例的属性访问和控制代理到代码内部另外一个实例类,将想对外公布的属性的访问和控制权交给代理类来操作,保留不想对外公布的属性的访问或控制权,比如只读访问,日志功能

  1. 在代理类中实现被代理的类属性访问和修改权限控制
  2. 异常捕获代理类的简化示例

代理类的一个简单的实现方式示例

目标:实现类Product的实例属性让另一个类Proxy来代理访问和控制,想将对外公布的属性交给代理类让外部访问和控制,不想对外公布的属性无法通过代理来访问和控制,这里不想对外公布的属性约定用下划线命名开头

# proxy_example1.py
# 以下是一个代理类实现只读访问的示例
# 目标:代理后只能访问和修改Product的公开属性,私有属性_current只能查看不能修改
class Product:def __init__(self, price, quantity):self.price = priceself.quantity = quantityself._current = 123# 只暴露代理类Proxy给外部使用
class Proxy:def __init__(self, obj):self._obj = objdef __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 = 1print(pp.abc, p.abc)pp._curr = 10000print(pp._curr)  # 私有属性,设置给了代理类print(p._curr)  # raise an error, 被代理的类Product的属性没有设置成功也无法访问def test_delattr():p = Product(10, 2)pp = Proxy(p)pp.abc = 123print(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_setattr()

输出

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 = priceself.quantity = quantityself._current = 123def __len__(self):return 111# 只暴露代理类Proxy给外部使用
class Proxy:def __init__(self, obj):self._obj = objdef __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)@propertydef __class__(self):    # 伪造__class__属性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:@classmethoddef dm1(cls, req, *args):print("dm1...", f"{req=}")print(1/0)      # 故意抛出异常return "dm1"class BLL:@classmethoddef bm1(cls, req):print("bm1...", f"{req=}")return DAL.dm1(req)class Application:def __init__(self, req):self.req = reqself._p = "private attr"def hd1(self):return BLL.bm1(self.req)class LoggerProxy:def __init__(self, obj):self._obj = objdef __getattr__(self, item):    # LoggerProxy类实例没获取到的属性会执行这个方法attr = getattr(self._obj, item)if callable(attr):  # 获取到了方法,则处理异常捕获@wraps(attr)def wrapped_method(*args, **kwargs):# print(f"Before accessing 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 methodreturn wrapped_methodelse:   # 获取到了属性,直接返回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双下划线开头和结尾的方法,如果想要被代理类访问和控制就必须在代理类中也定义对应的实际方法,另外,示例中主要是以下划线开头的方法作为私有属性的约定,也可以使用其他约定,这样在代理方法中的访问和修改时做出相应的判断即可

Python 代理类实现和控制访问与修改属性的权限相关推荐

  1. js在类的方法中访问自己的属性

    在类的方法中访问自己的属性,Javascript对于公有属性和私有属性的访问方法有所不同,请大家看下面的代码 functionShape(ax,ay) { varx=0; vary=0; this.g ...

  2. Python 创建类的成员并访问

    类的成员: python 中类的成员是有实例方法和数据成员组成 1 创建实例方法并访问 创建实例方法,就是创类类的时候实例化方法,具体的如下 class People:def __init__(sel ...

  3. Python把类当做字典来访问

    定义一个类将它实例化,我们可以通过obj.属性来访问类的属性,如果想获取类的所有实例变量,我们可以使用obj.__dict__来访问,如下: class A:def __init__(self):se ...

  4. win32 bios 类 对象编辑器 拒绝访问_面向对象的可复用设计模式之代理模式(13/24)...

    代理模式(Proxy pattern)就是给某一个对象提供一个代理,并由代理对象控制对原对象的引用.在一些情况下,一个客户不想或者不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作 ...

  5. python 私有和保护成员变量如何实现?—— 单下划线 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量; 双下划线 开始的是私有成员,意思是只有类对象自己能访问...

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量. 在python中定义私有变量只需要在变 ...

  6. 怎么用python画心_python怎么画心Python的类实例属性访问规则

    一般来说,在Python中,类实例属性的访问规则算是比较直观的. 但是,仍然存在一些不是很直观的地方,特别是对C++和Java程序员来说,更是如此. 在这里,我们需要明白以下几个地方: 1.Pytho ...

  7. python完全支持面向对象编程思想_面向对象的编程思想和Python的类,访问和属性,继承...

    本文将从访问限制,属性,继承,方法重写这几个方面继续介绍面向对象的编程思想和Python类的继承. 复制代码 一.访问权限: Python中在类的内部定义属性和方法,在类的外部是可以直接调用或进行访问 ...

  8. python 类和对象_面向对象的编程思想和Python的类,访问和属性,继承

    面向对象的编程思想和Python的类,类的方法和属性,实例方法这一文从面向对象的角度,介绍类的定义,类的属性和自定义方法. 本文将从访问限制,属性,继承,方法重写这几个方面继续介绍面向对象的编程思想和 ...

  9. [转载] python类内部成员的访问及外部访问(入门)

    参考链接: 在Python中更改类成员 class Student(): school = "xcxy"                    # 类属性 __major = &q ...

最新文章

  1. 一文搞清楚QPS、TPS、并发用户数、吞吐量
  2. 代码大全阅读笔记02
  3. .net remoting 技术
  4. 我是如何自学 Python 的,分享一下经验
  5. Qt creator5.7 OpenCV249之中值滤波(含源码下载)
  6. 11.* 指针 引用
  7. 数据:DeFi总锁仓量突破230亿美元
  8. js实现搜索框智能提示上下移动效果
  9. 2020-11-30 03_空域图像处理 笔记
  10. 如何在阿里云国际上设置邮箱主机
  11. html单位pt,CSS单位之pt和px的区别是什么
  12. 连锁企业——屈臣氏的经营模式
  13. 超级计算机阿波罗11,阿波罗11号制导计算机中指令模块和登月模块原始代码已在 GitHub 上开源...
  14. ListView 实现阻尼回弹效果 并去除边缘阴影
  15. 23.打印由*号组成的三角形图案
  16. woj 1537 Stones I
  17. 事件查看器出现Netlogon5723错误
  18. Flutter 中的应用内购买
  19. java计算机毕业设计基于ssm的基于android的家庭理财系统
  20. 使用shp数据批量裁剪栅格数据并统计均值

热门文章

  1. mac svn工具_Cornerstone 4 for mac(svn管理工具)
  2. Java线程池有哪些作用
  3. 大数据开发初学者学习路线
  4. 使用命名空间、头文件和实现文件
  5. java递归空瓶换饮料_问题描述:一次买n瓶可乐,k个空瓶可以换一瓶饮料,那么一共能喝多少瓶饮料? | 学步园...
  6. shell脚本自动备份MySQL数据库
  7. Selenium断言的使用,等待
  8. SpringBoot 封装返回类以及session 添加获取
  9. sort和qsort函数
  10. cmd导入mysql文件