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.pswp.cn/news/532113.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532113.shtml
英文地址,請注明出處:http://en.pswp.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…