今天来说一说代理模式。

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

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

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

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

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

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

首先我们编写一个名为 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()

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

  1. python中导入模块使用哪个关键字_关于python导入模块的关键字介绍

    关于python导入模块的关键字介绍 发布时间:2020-04-17 10:13:26 来源:亿速云 阅读:101 作者:小新 今天小编给大家分享的是关于python导入模块的关键字介绍,很多人都不太 ...

  2. python中对象和变量的关系_浅析python中的类变量和对象变量

    刚学python,学到了有关于类和对象的地方.对一个概念有点模糊,后来通过实践编码找到一定规律 在python中 class test(object): id=2 name='tt' list=['t ...

  3. python中format函数用法简书_从Python安装到语法基础,这才是初学者都能懂的爬虫教程...

    Python和PyCharm的安装:学会Python和PyCharm的安装方法 变量和字符串:学会使用变量和字符串的基本用法 函数与控制语句:学会Python循环.判断语句.循环语句和函数的使用 Py ...

  4. 在python中print表示的数据类型是_【Python连载】那些必须掌握的Python数据类型

    原标题:[Python连载]那些必须掌握的Python数据类型 经过近几年的发展,Python已成为了数据科学和机器学习的首选语言,许多人也因此开始走上自学python之路.从今天开始,本公众号将陆续 ...

  5. 在python中、关于全局变量和局部变量_在Python中,关于全局变量和局部变量,以下选项中描述不正确的是...

    [单选题]Python文本处理方向的第三方库是 [单选题]哪个选项的描述是正确的? [单选题]以下选项中属于Python语言中合法的二进制整数是 [单选题]以下说法错误的是: [单选题]下面属于B2B ...

  6. python中的打印是什么意思_对python:print打印时加u的含义详解

    对python:print打印时加u的含义详解 u:表示unicode字符串 不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码. 一般英文字符在使用各种编码下, 基本都 ...

  7. 在python中定义类时、运算符重载_自定义 Python 类中的运算符和函数重载(上)...

    如果你对 Python 中的str对象使用过 + 或 * 运算符,你一定注意到了它的操作与 int 或 float 类型的区别: 你可能想知道同一内置运算符或函数如何对不同类对象进行不同操作的.这分别 ...

  8. python中对象的概念是什么_简述Python中的面向对象编程的概念

    面向对象编程--Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...

  9. python中main.py是什么意思_关于python:什么是__main__.py?

    __main__.py文件是用来做什么的,我应该把什么类型的代码放进去,什么时候应该有一个? 通常,通过在命令行中命名.py文件来运行python程序: $ python my_program.py ...

最新文章

  1. 如何开展软件架构之需求分析3
  2. Promise.race 的原理
  3. Coursera课程Python for everyone:Quiz: Regular Expressions
  4. mysql基准性能测试标准_mysql性能测试与优化——(一),基准测试套件
  5. Airflow 中文文档:时区
  6. 几个商业模式的运用手法
  7. python画树干_python教你画一棵树
  8. Moss 2007 升级到 Moss2010 成功但界面仍然保持07?
  9. android 极光IM集成及使用
  10. Oracle 索引原理和种类
  11. win10不用密码登录及不显示“要使用本计算机,用户必须输入用户名和密码”的解决办法
  12. 计算机技术检索,计算机检索常用技术.ppt
  13. CLIENT_PLUGIN_AUTH is required 解决办法
  14. P3369 【模板】普通平衡树(fhq treap)
  15. 什么是单页网站设计?受欢迎的原因在哪?
  16. ai人工智能大爆发_人工智能解释了大爆炸之前发生的事情
  17. nyoj 547- Interesting Punch-Bowl(优先队列模拟)
  18. 指针进阶·八道笔试题(四)
  19. 自动化工程师如何快速上手上位机编程
  20. C语言程序——求学生总成绩和平均成绩

热门文章

  1. 三、项目经理的角色【PMP 】
  2. Flowable BPMN 用户手册 (v 6.5.0-SNAPSHOT)
  3. docker Gitlab14.5.0 安装、配置、部署、使用
  4. ByteArrayOutputStream和ByteArrayInputStream的简单使用
  5. cuda加速的头文件_如何从C ++头文件调用CUDA文件?
  6. C语言 函数指针和指针函数区别 - C语言零基础入门教程
  7. C语言逗号表达式 - C语言零基础入门教程
  8. oracle实验四运动会,实验四oracle的安全性和完整性控制
  9. lambda 流 peek java_JDK8 流与λ表达式
  10. php 登陆微博,PHP调用微博接口实现微博登录的方法示例