标签:关系 不同类 地址 rop 展示 标准输出 重写类 res 接受
在某些应用中,我们想要在访问某个对象之前执行一个或多个重要的操作,例如,访问敏感信息——在允许用户访问敏感信息之前,我们希望确保用户具备足够的权限。操作系统中也存在类似的情况,用户必须具有管理员权限才能在系统中安装新程序。
上面提到的重要操作不一定与安全问题相关。延迟初始化是另一个案例:我们想要把一个计算成本较高的对象的创建过程延迟到用户首次真正使用它时才进行。
这类操作通常使用代理设计模式( Proxy design pattern)来实现。该模式因使用代理(又名替代, surrogate)对象在访问实际对象之前执行重要操作而得其名。以下是四种不同的知名代理类型:
本文例子先是一个虚拟代理然后就是保护代理。
我们先创建一个LazyProperty类,用作一个修饰器。当它修饰某个特性时, LazyProperty惰性(首次使用时)加载特性,而不是立即进行。 __init__方法创建两个变量,用作初始化待修饰特性的方法的别名。 method变量是一个实际方法的别名,method_name变量则是该方法名称的别名。为更好理解如何使用这两个别名,可以将其值输出到标准输出(取消注释下面代码中的两个注释行)。
class LazyProperty:
def __init__(self, method):
self.method = method
self.method_name = method.__name__
# print(‘function overriden: {}‘.format(self.fget))
# print("function‘s name: {}".format(self.func_name))
LazyProperty类实际上是一个描述符。 描述符( descriptor)是Python中重写类属性访问方法( get()、 set()和__delete__())的默认行为要使用的一种推荐机制。 LazyProperty类仅重写了__set__(),因为这是其需要重写的唯一访问方法。换句话说,我们无需重写所有访问方法。 get()方法所访问的特性值,正是下层方法想要赋的值,并使用setattr()来手动赋值。 get()实际做的事情非常简单,就是使用值来替代方法!这意味着不仅特性是惰性加载的,而且仅可以设置一次。我们马上就能看到这意味着什么。同样,取消注释以下代码的的注释行,以得到一些额外信息。
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
Test类演示了我们可以如何使用LazyProperty类。其中有三个属性, x、 y和resource。我们想懒加载resource变量,因此将其初始化为None,如以下代码所示。
class Test:
def __init__(self):
self.x = ‘foo‘
self.y = ‘bar‘
self._resource = None
resource()方法是使用LazyProperty类修饰的。因演示目的, LazyProperty类将_resource属性初始化为一个tuple,如以下代码所示。通常来说这是一个缓慢/代价大的初始化过程(初始化数据库、图形等)。
@LazyProperty
def resource(self):
print(‘initializing self._resource which is: {}‘.format(self._resource))
self._resource = tuple(range(5)) # 假设这一行的计算成本比较大
return self._resource
main()函数展示了懒初始化是如何进行的。注意, get()访问方法的重写使得可以将resource()方法当作一个变量(可以使用t.resource替代t.resource())。
def main():
t = Test()
print(t.x)
print(t.y)
# 做更多的事情……
print(t.resource)
print(t.resource)
从该例子的输出我们可以得到如下结论:
foo
bar
initializing self._resource which is: None
(0, 1, 2, 3, 4)
(0, 1, 2, 3, 4)
可能对于该例子的输出有些读者还不能理解,这里做一补充。
在OOP中有两种基本的、不同类型的懒初始化,如下所示。
芯片(又名芯片密码)卡是现实生活中使用防护代理的一个好例子。借记/信用卡包含一个芯片, ATM机或读卡器需要先读取芯片;在芯片通过验证后,需要一个密码( PIN)才能完成交易。这意味着只有在物理地提供芯片卡并且知道密码时才能进行交易。
使用银行支票替代现金进行购买和交易是远程代理的一个例子。支票准许了对一个银行账户的访问。下图展示了支票如何用作一个远程代理。
Python的weakref模块包含一个proxy()方法,该方法接受一个输入对象并将一个智能代理返回给该对象。弱引用是为对象添加引用计数支持的一种推荐方式。
因为存在至少四种常见的代理类型,所以代理设计模式有很多应用案例,如下所示。
为演示代理模式,我们将实现一个简单的保护代理来查看和添加用户。该服务提供以下两个选项。
SensitiveInfo类包含我们希望保护的信息。 users变量是已有用户的列表。 read()方法输出用户列表。 add()方法将一个新用户添加到列表中。考虑一下下面的代码。
class SensitiveInfo:
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的一个保护代理。 secret变量值是客户端代码在添加新用户时被要求告知/提供的密码。注意,这只是一个例子。现实中,永远不要执行以下操作。
read()方法是SensetiveInfo.read()的一个包装。 add()方法确保仅当客户端代码知道密码时才能添加新用户。考虑一下下面的代码。
class Info:
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!")
main()函数展示了客户端代码可以如何使用代理模式。客户端代码创建一个Info类的实例,并使用菜单让用户选择来读取列表、添加新用户或退出应用。考虑一下下面的代码。
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))
完整代码:
class SensitiveInfo:
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))
class Info:
‘‘‘SensitiveInfo的保护代理‘‘‘
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!")
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()
输出如下:
1. read list |==| 2. add user |==| 3. quit
choose option: a
1. read list |==| 2. add user |==| 3. quit
choose option: 4
1. read list |==| 2. add user |==| 3. quit
choose option: 1
There are 4 users: nick tom ben mike
1. read list |==| 2. add user |==| 3. quit
choose option: 2
choose username: pet
what is the secret? blah
That‘s wrong!
1. read list |==| 2. add user |==| 3. quit
choose option: 2
choose username: bill
what is the secret? 0xdeadbeef
Added user bill
1. read list |==| 2. add user |==| 3. quit
choose option: 1
There are 5 users: nick tom ben mike bill
1. read list |==| 2. add user |==| 3. quit
choose option: 3
存在四种不同的代理类型,如下所示。
在第一个代码示例中,我们使用修饰器和描述符以地道的Python风格创建一个虚拟代理。这个代理允许我们以惰性方式初始化对象属性。
芯片卡和银行支票是人们每天都在使用的两个不同代理的例子。芯片卡是一个防护代理,而银行支票是一个远程代理。另外,一些流行软件中也使用代理。 Python有一个weakref.proxy()方法,使得创建一个智能代理非常简单。 ZeroMQ的Python实现则使用了远程代理。
标签:关系 不同类 地址 rop 展示 标准输出 重写类 res 接受
原文地址:https://www.cnblogs.com/JonnyJiang-zh/p/13223852.html