读懂python代码_读懂花里胡哨的14行Python代码!
最近在項目里發現一段代碼,初看比較難看懂,細看,也還是比較難看懂。遂研究了一下,證實了這段代碼確實,沒啥作用,遂刪之。記錄在下。
去掉幾個用于封裝的函數,保留最小代碼后,剩下以下14行代碼。你能說出這段代碼是干嘛的?每行代碼分別被執行了幾次嗎?
class CachedProperty():
def __init__(self,func,name=None):
self.func = func
self.name = name or func.__name__
def __get__(self,instance,cls=None):
res = instance.__dict__[self.name] = self.func(instance)
return res
class MyClass():
@CachedProperty
def caches(self):
return {}
mc = MyClass()
mc.caches['key'] = 1
print(mc.caches['key'])
要讀懂上面14行代碼,需要理解兩樣東西,一個是類裝飾器,一個是描述器。
類裝飾器
裝飾器比較常見,類裝飾器就不常見了。
給func1加上func2裝飾器,等同于令func1=func2(func1), func1依然是一個callable的對象。給func1加上class2裝飾器,同樣需要返回的是一個callable的對象。所以在class2中,要求實現__call__方法。
進群:960410445 即可獲取數十套PDF!
例子:
class log():
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
print('log...')
return self.func(*args, **kwargs)
@Log
def func1(x):
return x
以上就等同于令func1=Log(func1), 實際上就是一個callable的Log類實例了。調用func1的時候,就觸發__call__方法完成調用過程。
描述器
描述器是一個有"綁定行為"的對象屬性,用其他編程語言做一個較通俗的對比,就是實現了setter和getter的屬性。
定義一個描述器,需要為屬性至少定義__delete__, __get__, __set__中的任意一個方法。這些方法會在進行屬性訪問時自動被調用。
一個簡單的例子:
class Ds():
def __init__(self, v):
self.v = v
def __set__(self, obj, v):
self.v = v
def __get__(self, obj, objtype):
return self.v
class T():
x = Ds(10)
print(T.x) # 10
這里還要扯到一個優先級的問題, 如果只定義了__get__方法,那么當T.__dict__擁有同名屬性的時候,優先使用T.__dict__的,若還定義了__set__方法,則描述器永遠優先,稱為非資料描述器,反之稱為資料描述器。這一點對讀懂14行代碼至關重要。
代碼解釋
回到最初的14行代碼,可以說是'使用類裝飾器的方式來定義一個非資料描述器,利用優先級的特性,完成方法調用結果緩存的功能'。
一行行來看
class CachedProperty():
def __init__(self,func,name=None):
# func == caches
self.func = func
# self.name == 'caches'
self.name = name or func.__name__
# instance等于對象實例, 在本例子中,等同于mc,cls等同于MyClass
def __get__(self,instance,cls=None):
# 最右邊返回{},賦值給中間的mc.__dict__['caches']和最左邊的res
res = instance.__dict__[self.name] = self.func(instance)
return res
class MyClass():
# 類裝飾器, 和下面三行一起,等同于 caches = CachedProperty(lambda: {})
@CachedProperty
def caches(self): # 不需要被當成方法調用, 所以不需要實現__call__
return {}
mc = MyClass()
# 第一步,獲得mc.caches, 按優先級, mc.__dict__['caches']不存在,
# 所以觸發__get__方法調用,得到{}, 且mc.__dict__['caches']也等于{}
# 第二步,為mc.__dict__['caches']['key']賦值1
mc.caches['key'] = 1
# 按優先級,mc.__dict__['caches']已存在,不再觸發__get__方法和cahces(self)方法執行
# 取mc.__dict__['caches']['key'], 返回1。
print(mc.caches['key'])
以上,經過一通花里胡哨的操作,實際效用就是: 實現了caches(self)方法只會被執行一次,其結果將被緩存起來,供以后再次調用時使用。
在這個例子中,caches(self)只有一行代碼,返回{},在簡化前,也是大概這個意思,所以,在本項目中,這段代碼實際上毫無作用,遂可刪之。
之后,發現同樣的做法出現在django和flask等框架中,我的理解是,CacheProperty目的是為了實現view等較耗資源函數的結果緩存,并做到懶加載。例如,某類方法需要加載一個大模板并進行渲染,就可以使用CacheProperty, 保證在一次調用后,將結果緩存起來。若目標方法只是一個不耗計算資源的簡單方法,就沒必要再使用了。
總結
以上是生活随笔為你收集整理的读懂python代码_读懂花里胡哨的14行Python代码!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tensorflowgpu利用率为0_奥
- 下一篇: 李兴球python创意编程视频云盘_A3