python中代理模式分为几种_通俗 Python 设计模式——代理模式

今天来说一说代理模式。

代理模式顾名思义,是对资源进行代理访问的一种模式,这里的资源是泛指在程序中会使用到的数据、操作、过程、对象等等。当然,针对不同的资源,代理进行的操作不尽相同,根据前人的总结,有四种常见且知名的代理模式:

远程代理,即在物理上的远程资源(如服务器)在本地的代理

虚拟代理,即不是真实的代理,是对一些可以不在第一时间执行的操作进行的代理,他可以将比如复杂耗时的操作推迟到真正需要的时候再进行,所谓的惰性加载即是典型的虚拟代理模式

保护代理,即对敏感资源进行保护,在实际访问前,进行相应安全性控制的代理

智能代理,即引用代理,在资源被访问时,执行一些额外的预操作,如检查引用计数或线程安全之类的

书中提供了一个惰性加载的实例,来讲解虚拟代理,这里我们摘录于此。

首先我们编写一个名为 LazyProperty 的装饰器,他的作用在于,将他装饰的对象的执行时机从声明之时推后到被调用之时。LazyProperty 装饰器代码如下:

class LazyProperty(object):

def __init__(self, method):

self.method = method

self.method_name = method.__name__

print('function overriden: {}'.format(self.method))

print("function's name: {}".format(self.method_name))

def __get__(self, obj, cls):

if not obj:

return None

value = self.method(obj)

print('value {}'.format(value))

setattr(obj, self.method_name, value)

return value

这里我们为 LazyProperty 重写了 __get__ 方法,从代码中可以看出,__get__ 方法其实本质上是代理了被 LazyProperty 所修饰的对象的访问操作。也就是说,要访问被 LazyProperty 所修饰的对象,在实际返回其值之前,会先执行 LazyProperty.__get__ 方法。下面我们来验证一下。

编写一个 Test 类,假设其中 resource 操作会花费较长时间,代码如下:

class Test:

def __init__(self, x, y):

self.x = x

self.y = y

self._resource = None

def resource(self):

print('initializing self._resource which is: {}'.format(self._resource))

self._resource = tuple(range(x, y)) # 假设这一行的计算成本比较大

return self._resource

如果我们只是这样编写代码,那么每次在遇到需要使用 self._resource 时,调用 Test.resource 方法,都会需要重新执行一遍其中复杂的操作。如下代码所示:

def main():

t1 = Test(1,5)

t2 = Test(10,15)

print(t1.x)

print(t1.y)

print(t1._resource)

print(t2.x)

print(t2.y)

print(t2._resource)

# 做更多的事情……

print(t1.resource())

print(t2.resource())

print(t1._resource)

print(t2._resource)

print(t1.resource())

print(t2.resource())

main()

这段代码的输出是:

1

5

None

10

15

None

initializing self._resource which is: None

(1, 2, 3, 4)

initializing self._resource which is: None

(10, 11, 12, 13, 14)

(1, 2, 3, 4)

(10, 11, 12, 13, 14)

initializing self._resource which is: (1, 2, 3, 4)

(1, 2, 3, 4)

initializing self._resource which is: (10, 11, 12, 13, 14)

(10, 11, 12, 13, 14)

请注意其中两次出现的 initializing self._resource which is: 内容,这表明,每次调用 t.resource(),都重新执行了一次赋值操作。

然而当我们使用 LazyProperty 装饰器 & 描述符来修饰 Test.resource 方法时,修改 Test.resource 方法代码如下:

@LazyProperty

def resource(self):

print('initializing self._resource which is: {}'.format(self._resource))

self._resource = tuple(range(self.x, self.y)) # 假设这一行的计算成本比较大

return self._resource

如此一来,我们再将 main() 方法中,各处调用 t.resource() 改为 t.resource (因为这里 LazyProperty 已经充当了描述符,使得 t.resource 可以像访问属性一样直接访问),会发现输出内容有所改变,具体如下:

function overriden:

function's name: resource

1

5

None

10

15

None

initializing self._resource which is: None

value (1, 2, 3, 4)

(1, 2, 3, 4)

initializing self._resource which is: None

value (10, 11, 12, 13, 14)

(10, 11, 12, 13, 14)

(1, 2, 3, 4)

(10, 11, 12, 13, 14)

(1, 2, 3, 4)

(10, 11, 12, 13, 14)

除开最上面部分有一些之前没有见过的内容,我们可以明显的看到,初始化操作只执行了一次,之后的每次调用,都是直接获取已经初始化好的 Test._resource 属性。也就是说,本例中的虚拟代理 LazyProperty,一方面帮我们完成了惰性加载的操作,另一方面也充当了资源的描述符,方便其之后的获取其值。当然,根据需要,也可以在 LazyProperty 中实现 __set__ 等其他相关描述符操作。

本例完整代码如下:

class LazyProperty(object):

def __init__(self, method):

self.method = method

self.method_name = method.__name__

print('function overriden: {}'.format(self.method))

print("function's name: {}".format(self.method_name))

def __get__(self, obj, cls):

if not obj:

return None

value = self.method(obj)

print('value {}'.format(value))

setattr(obj, self.method_name, value)

return value

class Test(object):

def __init__(self, x, y):

self.x = x

self.y = y

self._resource = None

@LazyProperty

def resource(self):

print('initializing self._resource which is: {}'.format(self._resource))

self._resource = tuple(range(self.x, self.y)) # 假设这一行的计算成本比较大

return self._resource

def main():

t1 = Test(1,5)

t2 = Test(10,15)

print(t1.x)

print(t1.y)

print(t1._resource)

print(t2.x)

print(t2.y)

print(t2._resource)

# 做更多的事情……

print(t1.resource)

print(t2.resource)

print(t1._resource)

print(t2._resource)

print(t1.resource)

print(t2.resource)

main()

下面再将书中一个关于保护代理的实例摘录于此。

我们首先有一个需要访问的类,SensitiveInfo,其中包含了列表信息,一个读取列表信息的方法以及一个修改列表内容的方法:

class SensitiveInfo(object):

def __init__(self):

self.users = ['nick', 'tom', 'ben', 'mike']

def read(self):

print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))

def add(self, user):

self.users.append(user)

print('Added user {}'.format(user))

这里我们假设他的列表信息是需要保密访问的,只有获取密码后才能访问相应内容,那么我们在不修改这个类本身的情况下,要实现访问控制,就需要通过代理的方式来进行。增加一个 Info 类作为保护代理,他包含有与被保护对象 SensitiveInfo 相同的方法。代码如下:

class Info(object):

def __init__(self):

self.protected = SensitiveInfo()

self.secret = '0xdeadbeef'

def read(self):

self.protected.read()

def add(self, user):

sec = input('what is the secret? ')

self.protected.add(user) if sec == self.secret else print("That's wrong!")

这里的 Info 中,将被保护对象作为一个属性代理了起来,在要进行敏感操作(这里是修改被保护对象列表值)时,设定一系列验证等检测,来确保对被访问对象的操作时安全或者符合要求的。

我们依旧编写一个 main() 函数进行测试:

def main():

info = Info()

while True:

print('1. read list |==| 2. add user |==| 3. quit')

key = input('choose option: ')

if key == '1':

info.read()

elif key == '2':

name = input('choose username: ')

info.add(name)

elif key == '3':

exit()

else:

print('unknown option: {}'.format(key))

通过运行这个实例,可以看到保护代理是怎样实现保护这一核心操作的。

同时,书上还留了几道思考题,我摘录最有价值的一题于此。其实现代码将在本文最下方给出。

该示例有一个非常大的安全缺陷。没有什么能阻止客户端代码通过直接创建一个SensitveInfo实例来绕过应用的安全设置。优化示例来阻止这种情况。一种方式是使用abc模块来禁止直接实例化SensitiveInfo。在这种情况下,会要求进行其他哪些代码变更呢?

答案:

from abc import ABCMeta, abstractmethod

# 将类声明为抽象类,并为用 @abstractmethod 修饰相应的需要成为抽象方法的方法

# 如此一来,即无法直接将此类实例化,避免开发中的失误导致绕过代理,出现不安全的情况

class SensitiveInfo(metaclass=ABCMeta):

def __init__(self):

self.users = ['nick', 'tom', 'ben', 'mike']

@abstractmethod

def read(self):

'''read'''

pass

@abstractmethod

def add(self, user):

'''add'''

pass

class Info(SensitiveInfo):

'''SensitiveInfo的保护代理'''

def __init__(self):

# 通过这种方式,调用 SensitiveInfo.__init__() 获得 users 列表

super().__init__()

self.secret = '0xdeadbeef'

def read(self):

print('There are {} users: {}'.format(len(self.users), ' '.join(self.users)))

def add(self, user):

sec = input('what is the secret? ')

# 此时的操作全部基于从 SensitiveInfo 继承来的 users 进行

self.users.append(user) if sec == self.secret else print("That's wrong!")

def main():

info = Info()

while True:

print('1. read list |==| 2. add user |==| 3. quit')

key = input('choose option: ')

if key == '1':

info.read()

elif key == '2':

name = input('choose username: ')

info.add(name)

elif key == '3':

exit()

else:

print('unknown option: {}'.format(key))

if __name__ == '__main__':

main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

layer文件ajax上传,layer弹出层数据传输到content里面

在项目中使用layer弹出层的时候,遇到一个问题,就是利用ajax请求后台数据成功时,调用layer弹出层(iframe),如何把数据传到iframe里面去?经过百度,发现,使用js把数据拼接起来,然后在su…

清理offset_关于 kafka 日志清理策略的问题

现象:搭建了一个 kafka 服务, 使用 kafka-python 包正常生产数据, 但是 kafka 过五分钟就把我的 topic 删除掉. 但是配置 log 的已经配置了, 我认为 kafka 不应该删除我的 topic 历史数据.关于 log 清理的配置文件:############################# Log Flush Policy …

python面向对象思路_python面向对象方法

#需求:洗衣机,功能:能洗衣服#1、定义洗衣机类"""class 类名():代码"""classWasher():defwash(self):print("能洗衣服")#2 创建对象#对象名 类名()haier Washer()#3、验证成果#打印haier…

饥荒进地洞服务器无响应,饥荒联机洞穴设置及常见问题的解决方法

进阶篇服务端mod设置:首先(1)下载好要用mod,然后下载通用工具,解压放到DST的mod文件夹里运行(2)此时在文档\Klei\DoNotStarveTogether会多出一个文件modoverrides,把它复制到文档\klei\DoNotStarveTogether_EasyConfigCaves&#…

roads 构筑极致用户体验_万物互联大势所趋 华为保驾护航运营商“三个转型”...

随着通信技术及其应用的快速发展,人们发现物理世界和数字世界正在加速融合,数字经济正在改变和颠覆着传统市场格局。于是人们看到,电子商务、远程教育、远程医疗、物联网、大数据等等,一波接一波的商业浪潮不断涌现。然而支撑这一…

python列表字典_Python常用对字典、列表的操作

本文中使用的Python版本为3.x。合并两个列表方法一a [1, 2, 3]b [4, 5, 6]print(a b)print(a)print(b)输出结果为:[1,2,3,4,5,6][1,2,3][4,5,6]说明:“ab”后,a和b都没有变化。方法二a [1, 2, 3]b [4, 5, 6]a.extend(b)print(a)print(b)…

魔兽对战平台修改服务器数据,《魔兽争霸3》1.29补丁上线官方对战平台:平衡性大做改动...

IT之家3月1日消息 今天《魔兽争霸》官方微博宣布《魔兽争霸3》的最新补丁也就是1.29补丁已经登陆官方对战平台的PTR服务器上,想要尝鲜的用户可以前往官方对战平台进行更新和游玩。1.29补丁除了增加对于宽屏游戏的支持之外,还针对英雄单位进行平衡性的改动…

查询列名在哪张表_探索SQL-多表查询

一、表的加法(Union)1、用法:将两个表合并成一个表2、语句:select 查询结果 from 从哪张表查询 union select 查询结果 from 从哪张表查询*需保留重复行*select 查询结果 from 从哪张表查询 union all select 查询结果 from 从哪张…

使用未初始化的内存是什么意思_他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢...

这确实是个挺奇怪的问题,特别是当最常出现的几种解释理由都被排除后,看来JVM并没有耍一些明显的小花招:-Xmx和-Xms是相等的,因此检测结果并不会因为堆内存增加而在运行时有所变化。通过关闭自适应调整策略(-XX:-UseAdaptiveSizePo…

定义整型数组_C语言基础-数组怎么用

到目前为止,我们了解到C语言中可以使用整型,浮点型和字符型的数据类型来描述我们人类世界的各种数据,但是这些还远远不够……我们在IOT领域经常会遇到这样一个数据使用场景:某天的固定时间内,会有多台(我们…

找出一个字符串中出现次数最多的字_海量数据中找出前k大数(topk问题)

在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为top K问题。针对top K类问题,通常比较好的方案是分治Trie树/hash小顶堆(就是上面提到的最小堆),即先将数据集…

crowd counting_[crowd_counting]-SFCN-CVPR2019amp;amp;GCC dataset

1.Contribution(1)主要是提出了基于GTA5的GCC数据集数据集下载地址:https://gjy3035.github.io/GCC-CL/​gjy3035.github.io(2)提出了在如何在GCC上train,然后在传统的通用数据集上test的迁移学习方案&…

代码更换ui图片_用技术的方式,在UI设计稿中设置随机码,保证高清

本文首发于:行者AI 在工作中会遇到批量给图片添加文字,随机码等需求,当数据码数量较大时,UI的工作量就会非常大,这时候我们可以用python来帮我们提高工作效率。1. 需求分析我们有这样一张图片,我们需要将一…

hash地址_redis中的hash扩容、渐进式rehash过程

背景: redis字典(hash表)当数据越来越多的时候,就会发生扩容,也就是rehash对比:java中的hashmap,当数据数量达到阈值的时候(0.75),就会发生rehash,hash表长度变为原来的二…

是什么牌子_水晶项链什么牌子好

阅读本文前,请您先点击上面的蓝色字体,再点击“关注”,这样您就可以免费收到最新内容了。每天都有分享,完全是免费订阅,请放心关注! …

什么是机器人的五点校正法_机器人校正方法

机器人校正方法【专利说明】机器人校正方法[0001]本申请案主张于2012年9月18日申请之美国临时专利申请案第61/702,377号的优先权,所述专利申请案的揭示完整结合于此以供参考。技术领域[0002]本发明涉及一种工件加工,尤其涉及一种用于工件加工…

stn算子_深度学习常用算子(二)

1、Tensor维度变换1)Flatten作用:将输入tensor中从start_axis维度到end_axis维度合并为1维2)Reshape作用:将输入Tensor描述转换为新的shape3)FreespaceExtract作用:将h维变成1,其他维度不变,从而完成对h的采样&#xf…

iframe异步加载_5种延迟加载图像的方法以帮助你提升网站性能与用户体验

英文 | https://www.sitepoint.com/five-techniques-lazy-load-images-website-performance/翻译 | web前端开发(ID:web_qdkf)由于图像是Web上最流行也是必不可少的内容类型之一,因此网站上的图片页面加载时间很容易成为一个问题。即使进行了适当的优化&…

ubuntu18安装python3.6.8_ubuntu 18.04 + Python 3.6.8 更换软件安装源

国外的开源项目开展的是如火如荼,我们国内的当然也不甘落后。为了更好的玩转 Python,我使用了 ubuntu Linux 来作为开发环境。但是由于国内网络的限制,访问国外的一些软件源的时候,速度比较慢,这时我们需要更换成国内的…

springframework报错_应对报错信息的必杀技!

今天遇到了一个错误,一般的错误提示会很明显,一看就知道是什么问题。今天遇到的这个说实话真的不好找原因,一般在这种情况下该怎么解决呢?分享下我的思路吧,不一定是最好的,至少有用。直接上图吧&#xff0…