Python的代理类实现,控制访问和修改属性的权限你都了解吗
作者:Moelimoe 发布时间:2022-10-25 13:09:47
本篇文章主要内容
代理类主要功能是将一个类实例的属性访问和控制代理到代码内部另外一个实例类,将想对外公布的属性的访问和控制权交给代理类来操作,保留不想对外公布的属性的访问或控制权,比如只读访问,日志功能
1.代理类实现被代理类的属性访问和修改权限控制
2.异常捕获代理类的简化示例
代理类的一个简单的实现方式示例
目标:实现类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.
来源:https://blog.csdn.net/Moelimoe/article/details/123609039
猜你喜欢
- 首先来看看这个php字符串替换函数 strtr()的两种用法:strtr(string,from,to) 或者strtr(string,ar
- Celery文档参考:http://docs.jinkan.org/docs/celery/参考文章:https://www.jb51.ne
- 问题你想使用一个装饰器去包装函数,但是希望返回一个可调用的实例。 你需要让你的装饰器可以同时工作在类定义的内部和外部。解决方案为了将装饰器定
- python爬虫设置每个代理ip的方法:1.添加一段代码,设置代理,每隔一段时间换一个代理。urllib2 默认会使用环境变量 http_p
- IE 的 Cookie 文件保存在 ?:\Documents and Settings\<user>\Cookies 目录,后缀
- 安装TensorFlow在Windows上,真是让我心力交瘁,想死的心都有了,在Windows上做开发真的让人发狂。首先说一下我的经历,本来
- var iframe = document.createElement("iframe"); iframe.id = &
- 而由于InnoDB是一个健壮的事务型存储引擎,已经有10多年的历史,一些重量级的互联网公司(Yahoo,Google Netease ,Ta
- 1、方法说明import cv2 as cvimg1 =cv.imread(filename[, flags])参数说明filename图片
- 栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
- 问题最近在研究图学习,在用networkx库绘图的时候发现问题。'''author:zhengtime:2020.1
- 本文研究的主要是python模块之paramiko的相关用法,具体实现代码如下,一起来看看。paramiko模块提供了ssh及sft进行远程
- 前言metrics用于判断模型性能。度量函数类似于损失函数,只是度量的结果不用于训练模型。可以使用任何损失函数作为度量(如logloss等)
- 以前我就是一篇博文 就给出一个好用的函数,它在我几篇博文中被广泛运用的。最近看了不少东西,于是便有了这篇博文,以梳理我学到的新东西。毫无疑问
- 在讨论其返回值前,我们先来介绍以下calcHist()函数的用法:cv2.calcHist()函数cv2.calcHist()函数的作用通过
- datetime 时间包认识 datetime 时间包:date:日期;time:时间;所以 datetime 就是 日期与时间的结合体使用
- 我们在前面已经分别介绍了如何在spring Boot中使用JPA以及如何在Spring Boot中输出REST资源。那么关于数据库访问还有一
- 自己从工艺品设计到平面设计到网络设计,虽然设计原则不离其宗,但经验下来的心得告诉自己,设计媒介的变化带来很多媒介自身的特殊性,下面总结下网站
- PyGame是一个Python的库,能够让你更容易的写出一个游戏。它提供的功能包括图片处理和声音重放的功能,并且它们能很容易的整合进你的游戏
- Pyinstallerpyinstaller是python的一个第三方模块,使用它可以将python程序打包为可执行文件,实现打包后的程序在