Java解決CAS機制中ABA問題的方案
一. 什麼是裝飾器
知乎某大佬如是說:內褲可以用來遮羞,但是到了冬天它沒法為我們防風禦寒,聰明的人們發明了長褲,有了長褲後寶寶再也不冷了,裝飾器就像我們這裡說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。
裝飾器本質上是Python函式,可以為已存在的物件新增額外的功能,同時裝飾器還可以抽離出與函式無關的重用程式碼。具體應用場景如:插入日誌、效能測試、事務處理、快取、許可權校驗等。
換言之
裝飾器不能影響原函式的功能,裝飾器是獨立出來的函式。誰呼叫它,誰就可以使用它的功能。
二.舉個栗子
add的功能是計算x和y的值,我們稱作功能函式。
logger的作業是在執行add函式的同時再列印了其他的資訊,這部分的作為add的功能增強,我們稱為裝飾。
1.引子
#功能函式 def add(x,y): return x+y #裝飾函式 def logger(fn): print('frist') x = fn(4,5) print('second') return x print(logger(add)) #把函式add傳給logger ,return x+y #print('frist') #print('secend') # x = fn(4,5) ==> x = 4 y= 5 x= 4+5 = 9 #return 9
frist
second
9
2.提取引數
x,y的引數都放在logger函式內部了,影響函式的靈活性,此處我們可以提取出來。
def add(x,y): return x + y def logger(fn,*args,**kwargs): print('frist') x = fn(*args,**kwargs) print('second') return x print(logger(add,1,y=11))
frist
second
12
3.柯里化
def add(x,y): return x + y def logger(fn): def wrapper(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper print(logger(add)(5,y=11))
begin
end
16
懵逼ing
以下為個人理解,左邊為非柯里化函式,右邊是柯里化函式。
柯里化函式
前面說過柯里化的定義,本來可以一次傳入兩個引數,柯里化之後。只需要傳入一個函數了。。
左邊傳入add 和 兩個引數。
右邊的logger(add)是一個函式,只需要傳入兩個引數。logger(add)是個整體,結合成一個函式。當然這樣寫,我們看函式主題的部分也是不一樣的。
函式的基礎中說過,函式的傳參必須和函式引數的定義一致。重點分析右邊函式(柯里化)。
引數部分:引數傳入的方式,logger函式需要傳入個fn,fu的返回值是wrapper函式,wrapper函式的引數是(*args,**kwargs)所以此次就需要分兩次傳入引數。
第一次傳入fn,再次傳入wrapper函式需要的引數。所以就出現了最下邊的呼叫方式。
print(logger(add)(5,y=50))。
返回值部分:右側的logger函式是個巢狀函式,logger的返回值是wrapper,內層的wrapper函式返回值是x,x = fn(*args,**kwargs)。fn函式是最後呼叫時候傳入的add函式。
懵逼 X 2。。。。
def add(x,**kwargs): def logger(fn): #引數剝離 def newfunction(*args,**kwargs): #新定義一個函式,logger函式返回也是這個函式名字 print('frist') print('frist') x = fn(*args,**kwargs) == > x = fn(*args,**kwargs) print('second') print('second') return x return x return newfunction print(logger(add,y=11)) print(logger(add)(5,y=11)) #兩次傳入引數
效果如下:
def add(x,y): return x + y def logger(fn): #引數剝離 def newfunction(*args,**kwargs): #新定義一個函式,logger函式返回也是這個函式名字 print('frist') x = fn(*args,**kwargs) print('second') return x return newfunction print(logger(add)(5,y=11)) #兩次傳入引數
frist
second
16
繼續懵逼的話就這樣用吧。。。用多了就悟道了。。
4.裝飾器語法糖
#再次變形。。。 def add(x,y): return x + y def logger(fn): def wrapper(*args,**kwargs) print('end') return x return wrapper ##呼叫方法1: print(logger(add)(x=1111,y=1)) ##呼叫方法2: add = logger(add) print(add(x=11,y=3)) ##呼叫方法3: python給我們的語法糖 @logger # 說明下邊的函式,add 其實是 add = logger(add) def add(x,y): return x + y print(add(45,40))
begin
end
1112
begin
end
14
begin
end
85
三.複雜的栗子
import datetime import time def logger(fn): def warp(*arges,**kwarges): print("arges={},kwarges={}".format(arges,kwarges)) #列印函式的兩個引數 start = datetime.datetime.now() #獲取函式執行的開始時間 ret = fn(*arges,**kwarges) #傳入兩個引數,呼叫add函式 此處有個return的值,需要一層一層的返回出去 duratime = datetime.datetime.now() - start #獲得函式的執行時間 print("function {} took {}s".format(fn.__name__,duratime.total_seconds())) #列印函式的執行時間 return ret #返回fn的結果 ,fn = x+y ==> 返回x+y的值。 x = 4 y= 11 ==> return 11 return warp #返回warp的 return ==> ret 的return ==> return 11 函式的最終結果為11 @logger def add(x,y): print("oooooook") time.sleep(1.5) return x+y print(add(4,y=11)) #如果充分理解了每個小部件,這個簡單的完整版本也是很好理解的了。 #1,logger是個裝飾器,而且使用了柯里化技術 #2,add 傳參給logger的fn 形參,add(4,y=5)的兩個引數傳入給warp函式的兩個形參 # #
arges=(4,),kwarges={'y': 11} oooooook function add took 1.5017s 15
再次翻譯
import datetime import time #####################################裝飾開始############################################ def logger(fn): #拿到函式名稱 def warp(*arges,**kwarges): #拿到函式帶過來的引數開始裝飾 print("arges={},kwarges)) #來試試列印兩個引數 start = datetime.datetime.now() # ret = fn(*arges,**kwarges) # 此處呼叫add函式。開始執行函式,發現return語句。。ret的結果就是return。 duratime = datetime.datetime.now() - start # print("function {} took {}s".format(fn.__name__,duratime.total_seconds())) return ret #加工完成開始返回。warp的返回值是ret ,ret的返回值是 add函式的執行結果(原函式的功能完整的保留了) return warp # logger的返回結果是warp,warp的返回值是ret ,ret的返回值是 add函式的執行結果(原函式的功能完整的保留了) #####################################裝飾完成############################################ @logger #裝飾工廠 ######add是需要被裝飾的函式,當你有這個想法的事情,其實事情已經開始發生了。 def add(x,y): # 此時add = logger(add) 此處前面的@logger標記就是想要讓logger裝飾器像一個工廠一樣對add函式進行加工。 print("oooooook") time.sleep(1.5) return x+y print(add(4,y=11))
arges=(4,kwarges={'y': 11} oooooook function add took 1.501604s 15
四.帶參裝飾器
1. 文件字串
我們約定,在python函式的第一行需要對函式進行說明,使用三引號表示。
如果是英文說明,慣例首字母大寫,第一行寫概述,空一行,第三行寫詳細描述。
如果函式中有文件字串,預設會放在函式的doc屬性中,可以直接訪問。
def add(x,y): """This is a function of addition""" a = x+y return x + y print("function name is {} function doc = {} ".format(add.__name__,add.__doc__)) print(help(add)) function name is add function doc = This is a function of addition Help on function add in module __main__: add(x,y) This is a function of addition None
2. 前面裝飾器的副作用
前面裝飾器基本上已經可以完成對函式進行加強的功能了,但是還有些瑕疵。比如原來函式的原屬性已經被替換為裝飾器的屬性了。如下:
def add(x,y): return x + y def logger(fn): "This is logger doc" def wrapper(*args,**kwargs): "This is wrapper doc" print('begin') x = fn(*args,**kwargs) print('end') return x return wrapper @logger # add = logger(add) def add(x,y): "This is add doc " print("name = {} doc = {}".format(add.__name__,add.__doc__)) return x + y print(add(45,40)) #可以看出來add被裝飾出來的函式(新的add)的屬性已經全部改變了。
begin
name = wrapper
doc = This is wrapper doc
end
85
3. 解決方案一
三個函式:
第一個:copy原函式的屬性 copy_properties
第二個:裝飾器 logger
第三個:功能函式 add
def copy_properties(src,dst): # 把src的相關屬性賦值給dst (fn,wrap) dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): """'This is a function of logger'""" def wrap(*arges,**kwarges): # """'This is a function of wrap'""" print('<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>') x = fn(*arges,**kwarges) #print("name={} doc={}".format(add.__name__,add.__doc__)) print('<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>') return x copy_properties(fn,wrap) #思考1:為什麼放在這個位置呼叫 return wrap @logger def add(x,y): """'This is a function of add'""" print("name={} doc={}".format(add.__name__,add.__doc__)) return x+y print(add(4,6))
<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>
name=add
doc='This is a function of add'
<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>
10
4. 解決方案二
但凡使用裝飾器都會出現屬性的這個問題,為什麼不把copy_properties也做成裝飾器呢?
三個函式:
第一個:copy原函式的裝飾器 copy_properties1
第二個:裝飾器 logger
第三個:功能函式 add
def copy_properties(src,wrap) dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ #利用前面的知識我們可以對copy_properties輕鬆進行變形 def copy_properties1(src): # 把src的相關屬性賦值給dst (fn,wrap) def _copy(dst): dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ return dst return _copy
帶參裝飾器:
def logger(fn): """'This is a function of logger'""" @copy_properties1(fn) #wrap = copy_properties(fn)(wrap) #== > 柯里化 兩次傳入引數 src = fn , dst = wrap 新的wrap函式的屬性已經替換為原函式的。 def wrap(*arges,**kwarges): #wrap = copy_properties(fn)(wrap)(*arges,**kwarges) """'This is a function of wrap'""" print('>->->->->->->->->->->->->->->->->->->->->->->->->->') x = fn(*arges,**kwarges) print('<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<') return x return wrap @logger #add =logger(add) def add(x,add.__doc__)) return x+y print(add(4,11))
以上就是詳解Python 裝飾器的詳細內容,更多關於Python 裝飾器的資料請關注我們其它相關文章!