LeetCode 5512. 統計不開心的朋友 模擬 雜湊
一:裝飾器簡介
1.1 什麼是裝飾器:
'裝飾'代指為被裝飾物件新增新的功能,'器'代指器具/工具,裝飾器與被裝飾的物件均可以是任意可呼叫物件。概括地講,裝飾器的作用就是在不修改被裝飾物件原始碼和呼叫方式的前提下為被裝飾物件新增額外的功能。
1.2 為什麼要用裝飾器:
軟體的設計應該遵循開放封閉原則,即對擴充套件是開放的,而對修改是封閉的。對擴充套件開放,意味著有新的需求或變化時,可以對現有程式碼進行擴充套件,以適應新的情況。對修改封閉,意味著物件一旦設計完成,就可以獨立完成其工作,而不要對其進行修改。
軟體包含的所有功能的原始碼以及呼叫方式,都應該避免修改,否則一旦改錯,則極有可能產生連鎖反應,最終導致程式崩潰,而對於上線後的軟體,新需求或者變化又層出不窮,我們必須為程式提供擴充套件的可能性,這就用到了裝飾器。
1.3 語法糖:
在Python語法中,為了更簡潔而優雅的使用裝飾器,專門提供了一種語法來取代index=timer(index)的形式,需要在被裝飾物件的正上方單獨一行新增@timer,當直譯器解釋到@timer時就會呼叫timer函式,且把它正下方的函式名當做實參傳入,然後將返回的結果重新賦值給原函式名
@timer # index=timer(index) def index(): time.sleep(3) print('Welcome to the index page') return 200
如果在一個函式中需要使用多個裝飾器,可以疊加呼叫,疊加多個裝飾器也無特殊之處,載入順序自上而下,執行順序自上而下,下面會詳細說明
裝飾器的實現原理有一些類似於偷樑換柱的概念,即將原函式名指向的記憶體地址偷樑換柱成warpper函式,因此應該將warpper做的和原函式一樣才行
二:裝飾器的實現
函式裝飾器分為:無參裝飾器和有參裝飾兩種,二者的實現原理一樣,都是’函式巢狀+閉包+函式物件’的組合使用的產物。
2.1 無參裝飾器
def outter(func):
def wrapper(*args,**kwargs):
# 1、呼叫原函式
# 2、為其增加新功能
return res
return wrapper
2.2 有參裝飾器
由於語法糖@的限制,outter函式只能有一個引數,並且該引數只用來接收被裝飾物件的記憶體地址
def 有參裝飾器(x,y,z): def outter(func): def wrapper(*args, **kwargs): res = func(*args, **kwargs) return res return wrapper return outter @有參裝飾器(1,y=2,z=3) def 被裝飾物件(): pass
可以使用help來檢視函式的文件註釋,本質就是檢視函式的doc屬性,但是對於被裝飾之後的函式,檢視文件註釋
print(help(home))
列印結果: Help on function wrapper in module __main__: wrapper(*args, **kwargs) None
2.3 裝飾器的修正
若想要保留原函式的文件和函式名屬性,需要修正裝飾器 def timer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res wrapper.__doc__=func.__doc__ wrapper.__name__=func.__name__ return wrapper 手動將原函式的屬性賦值給warpper函式: 1、函式wrapper.__name__ = 原函式.__name__ 2、函式wrapper.__doc__ = 原函式.__doc__ wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ Python中有一個專門的模組functools下提供了一個裝飾器warps專門用來幫我們實現這件事 from functools import wraps def timer(func): @wraps(func) def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper
2.4 裝飾器的疊加
類裝飾器: 無參類裝飾器: class Kuozhan(): def __call__(self, func): # 把物件當作函式呼叫的時候自動觸發 return self.kuozhan2(func) def kuozhan1(func): def newfunc(): print("考試前,認真複習") func() print("考試後,咋咋呼呼") return newfunc def kuozhan2(self,func): def newfunc(): print("考試前,開開心心") func() print("考試後,又哭又鬧") return newfunc @Kuozhan.kuozhan1 def func(): print("考試中......") func() print(">>>>>>>>>>>>>>>>>") @Kuozhan() def func(): print("考試中......") func() 有參類裝飾器: class Kuozhan(): money = "故宮門票,每人100一次," def __init__(self,num): self.num = num def __call__(self, cls): if self.num == 1: return self.newfunc1(cls) elif self.num == 2: return self.newfunc2(cls) def ad(self): print("故宮一般指北京故宮。北京故宮是中國明清兩代的皇家宮殿,舊稱紫禁城,位於北京中軸線的中心。北京故宮以三大殿為中心....") def newfunc1(self,cls): def newfunc(): cls.money = Kuozhan.money cls.ad = Kuozhan.ad return cls() return newfunc def newfunc2(self,cls): def newfunc(): if "run" in cls.__dict__: res = cls.run() cls.run = res return cls() return newfunc @Kuozhan(1) class MyClass(): def run(): return "程式執行成功。。。" obj = MyClass() print(obj.money) obj.ad() @Kuozhan(2) class MyClass(): def run(): return "程式執行成功。。。" obj = MyClass() print(obj.run)