python中代理模式分为几种_通俗 Python 设计模式——代理模式
今天來說一說代理模式。
代理模式顧名思義,是對資源進行代理訪問的一種模式,這里的資源是泛指在程序中會使用到的數據、操作、過程、對象等等。當然,針對不同的資源,代理進行的操作不盡相同,根據前人的總結,有四種常見且知名的代理模式:
遠程代理,即在物理上的遠程資源(如服務器)在本地的代理
虛擬代理,即不是真實的代理,是對一些可以不在第一時間執(zhí)行的操作進行的代理,他可以將比如復雜耗時的操作推遲到真正需要的時候再進行,所謂的惰性加載即是典型的虛擬代理模式
保護代理,即對敏感資源進行保護,在實際訪問前,進行相應安全性控制的代理
智能代理,即引用代理,在資源被訪問時,執(zhí)行一些額外的預操作,如檢查引用計數或線程安全之類的
書中提供了一個惰性加載的實例,來講解虛擬代理,這里我們摘錄于此。
首先我們編寫一個名為 LazyProperty 的裝飾器,他的作用在于,將他裝飾的對象的執(zhí)行時機從聲明之時推后到被調用之時。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
這里我們?yōu)?LazyProperty 重寫了 __get__ 方法,從代碼中可以看出,__get__ 方法其實本質上是代理了被 LazyProperty 所修飾的對象的訪問操作。也就是說,要訪問被 LazyProperty 所修飾的對象,在實際返回其值之前,會先執(zhí)行 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 方法,都會需要重新執(zhí)行一遍其中復雜的操作。如下代碼所示:
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(),都重新執(zhí)行了一次賦值操作。
然而當我們使用 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 可以像訪問屬性一樣直接訪問),會發(fā)現輸出內容有所改變,具體如下:
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)
除開最上面部分有一些之前沒有見過的內容,我們可以明顯的看到,初始化操作只執(zhí)行了一次,之后的每次調用,都是直接獲取已經初始化好的 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))
通過運行這個實例,可以看到保護代理是怎樣實現保護這一核心操作的。
同時,書上還留了幾道思考題,我摘錄最有價值的一題于此。其實現代碼將在本文最下方給出。
該示例有一個非常大的安全缺陷。沒有什么能阻止客戶端代碼通過直接創(chuàng)建一個SensitveInfo實例來繞過應用的安全設置。優(yōu)化示例來阻止這種情況。一種方式是使用abc模塊來禁止直接實例化SensitiveInfo。在這種情況下,會要求進行其他哪些代碼變更呢?
答案:
from abc import ABCMeta, abstractmethod
# 將類聲明為抽象類,并為用 @abstractmethod 修飾相應的需要成為抽象方法的方法
# 如此一來,即無法直接將此類實例化,避免開發(fā)中的失誤導致繞過代理,出現不安全的情況
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 设计模式——代理模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 穷和抠门是两回事 人穷心不能穷
- 下一篇: 魔借查征信吗