在v站看到一篇文章,說到了golang實(shí)現(xiàn)python裝飾器的方法,有兩個(gè)實(shí)現(xiàn)方式
其中一種是用類方式實(shí)現(xiàn)的,當(dāng)時(shí)泛覽了下代碼,覺得 emmm...實(shí)現(xiàn)方式挺花哨的,貌似看懂了就沒怎么在意,當(dāng)時(shí)特意把代碼復(fù)制到goland中作為參考了
某天上班摸魚,仔細(xì)看了下這段代碼,梳理細(xì)節(jié)后忽然發(fā)現(xiàn)沒get到其中邏輯
郁悶很久未參透
有空就特意調(diào)試打印下這段代碼
好幾天才整明白,這大概就是菜比の理解能力了吧

代碼如下:
// with interface. which we need create a `point` first. and use it later
package main
import "fmt"
type FibI interface {
Fib(n int) int
Wrap(fib FibI) FibI
}
type Fib struct {
Wrapper FibI
}
func (this *Fib) Fib(n int) int {
//wrapper := this.Wrapper
if this.Wrapper == nil {
this.Wrapper = this
}
fmt.Printf("我執(zhí)行了...%T...%v\n", this.Wrapper, n)
if n == 0 {
return 0
}
if n == 1 {
return 1
}
// call son
fmt.Printf("leixing9...%T...\n", this.Wrapper)
return this.Wrapper.Fib(n-1) + this.Wrapper.Fib(n-2)
}
func (this *Fib) Wrap(fib FibI) FibI {
fmt.Printf("leixing1...%T...\n", this.Wrapper)
this.Wrapper = fib
fmt.Printf("leixing2...%T...\n", this.Wrapper)
fmt.Println("fib 調(diào)用", this)
return this
}
type CacheFib struct {
Wrapper FibI
cache map[int]int
}
func (this *CacheFib) Wrap(fib FibI) FibI {
this.Wrapper = fib
fmt.Printf("leixing6...%T...\n", this.Wrapper)
return this
}
func (this *CacheFib) Fib(n int) int {
if this.cache == nil {
this.cache = make(map[int]int)
}
if ans, ok := this.cache[n]; ok {
return ans
}
ans := this.Wrapper.Fib(n)
this.cache[n] = ans
return ans
}
type CounterFib struct {
Wrapper FibI
Counter int
}
func (this *CounterFib) Wrap(fib FibI) FibI {
this.Wrapper = fib
fmt.Printf("leixing...%T...\n", this.Wrapper)
return this
}
func (this *CounterFib) Fib(n int) int {
this.Counter++
return this.Wrapper.Fib(n)
}
func main() {
fib := new(Fib)
//fmt.Println("result fib", fib.Fib(10))
cacheFib := new(CacheFib)
//f := cacheFib.Wrap(fib.Wrap(cacheFib))
//fmt.Println(f.Fib(10), "heihei")
counterFib := new(CounterFib)
counterCacheFib := cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))
fmt.Println("result cache:counter:fib", counterCacheFib.Fib(10))
fmt.Printf("count: %d, cache: %v", counterFib.Counter, cacheFib.cache)
}
本文的目的沒有那么高大上,就是簡(jiǎn)單的理解一下,這種裝飾器實(shí)現(xiàn)及其后的核心原理
講真,這個(gè)代碼實(shí)現(xiàn)的挺花哨的>_<
所以,我把自己的那些調(diào)試打印信息也一并給出了
首先第一步:
背景:用裝飾器的原理實(shí)現(xiàn)斐波那契數(shù)列的計(jì)算
如果有g(shù)olang語(yǔ)法基礎(chǔ),相信這些main函數(shù)之前的代碼應(yīng)該理解起來不難
其中Fib結(jié)構(gòu)是一個(gè)實(shí)現(xiàn)了FibI接口的對(duì)象
乍看之下好像僅此而已
CacheFib結(jié)構(gòu)也同樣實(shí)現(xiàn)了此接口,不過提供了一個(gè)map數(shù)據(jù)結(jié)構(gòu),會(huì)把之前計(jì)算過的數(shù)值緩存起來,后面調(diào)用直接取緩存
CounterFib結(jié)構(gòu)就簡(jiǎn)單啦,它計(jì)算了CounterFib實(shí)例調(diào)用Fib方法的次數(shù)
三個(gè)Fib類的Wrap方法實(shí)現(xiàn)方式都一樣:就是把自身的Wrapper屬性賦值為參數(shù)中的(實(shí)現(xiàn)了)FibI值
一直到main函數(shù)之前,這些代碼還都不難理解
最后注意下主函數(shù)內(nèi)的調(diào)用方法
cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))
瞬間懵逼有木有
乍看之下,為什么cacheFib會(huì)出現(xiàn)兩次,看起來好像是自己引用了自己(遞歸也是自己調(diào)用自己,好好參悟一下)
嗯,那些能明白這行代碼的童鞋可以先行告退了,沒懂得同學(xué)咱門繼續(xù)
不明白的話就運(yùn)行一遍代碼:
斐波那契數(shù)列計(jì)算完美,有計(jì)算次數(shù),也優(yōu)化了計(jì)算邏輯(緩存了)
但這并不妨礙咱們的懵逼狀態(tài)ing~
整個(gè)方法的調(diào)用鏈?zhǔn)窃鯓拥哪?/p>
我在各個(gè)位置加了打印信息,其核心就是Fib方法
通過打印可以發(fā)現(xiàn),每次計(jì)算n數(shù)值的時(shí)候都會(huì)調(diào)用Fib 對(duì)象的Fib 方法(有點(diǎn)繞)
打印Fib 對(duì)象的Wrapper類型, 顯示為main.CacheFib類型
所以,我們重新回顧下Fib對(duì)象,其實(shí)它沒那么簡(jiǎn)單,首先對(duì)象的Wrapper 屬性本身類型就是實(shí)現(xiàn)了FibI 接口的類型,其次對(duì)象本身也實(shí)現(xiàn)FibI 接口
Fib 對(duì)象的Fib 方法中調(diào)用了 Fib 對(duì)象的Wrapper屬性的Fib方法(還是很繞,注意區(qū)分下Fib對(duì)象和Fib方法,這是兩個(gè)概念)
那么回頭來看main函數(shù)中的實(shí)現(xiàn),cacheFib 是CacheFib類的實(shí)例,cacheFib使用Wrap方法用counterFib對(duì)象來裝飾自身,但是counterFib對(duì)象的Wrapper被fib對(duì)象裝飾了,同時(shí)fib對(duì)象又被cacheFib自身裝飾了,那么當(dāng)我們執(zhí)行counterCacheFib.Fib(10)是時(shí)候,經(jīng)過層層裝飾,其實(shí)調(diào)用的方法是fib對(duì)象的Fib方法,fib對(duì)象的Fib方法在每次執(zhí)行的時(shí)候又會(huì)調(diào)用自身Wrapper的Fib方法(不過這是一個(gè)類似遞歸的操作,只有當(dāng)參數(shù)n大于1的時(shí)候才會(huì)調(diào)用自身的Wrapper的Fib方法),不過Fib.Wrapper又被cacheFib裝飾了,所以繞了一圈又回來了,只要入?yún)大于1,就會(huì)一直遞歸往復(fù)(這是一個(gè)有退出條件的閉環(huán)), 所以小伙伴們都繞明白了嗎
如果用python實(shí)現(xiàn)起來其實(shí)就相對(duì)簡(jiǎn)單多了,這里就不再給出代碼了。換個(gè)思路,為了加強(qiáng)理解,如果用python實(shí)現(xiàn)一個(gè)上面golang風(fēng)格的代碼呢?感興趣可以實(shí)現(xiàn)一下,會(huì)更好的幫助你理解golang以及python的風(fēng)格用法特點(diǎn),這里給出參考:
class CacheFib(object):
def __init__(self):
self.cache = dict()
self.f = None
def wrap(self, obj):
self.f = obj.fib
# return self
def fib(self, n):
if not self.cache.get(n):
self.cache[n] = self.f(n)
return self.cache[n]
class CountFib(object):
def __init__(self):
self.count = 0
self.f = None
def wrap(self, obj):
self.f = obj.fib
# return self
def fib(self, n):
self.count += 1
return self.f(n)
class Fib(object):
def __init__(self):
self.f = self.fib
def wrap(self, obj):
self.f = obj.fib
# return self
def fib(self, n)->int:
if n == 0:
return 0
elif n == 1:
return 1
else:
return self.f(n-1) + self.f(n-2)
if __name__ == '__main__':
f = Fib()
cache = CacheFib()
count = CountFib()
f.wrap(cache)
count.wrap(f)
cache.wrap(count)
# cache = cache.wrap(count.wrap(f.wrap(cache))
cache.fib(20)
print("count: {}, cache: {}".format(count.count, cache.cache) )