2017-05-23 36 views
2

我試圖構建我的第一個裝飾器並在類中實現它。python3從類到裝飾器的自變量

# decorator class 
class Cache(object): 
    def __init__(self,filename,**kwargs): 
    self.time_ago = datetime.now() - timedelta(**kwargs) 
    self.filename = filename 

    def __call__(self,fn): 
    if not os.path.isfile(self.filename): 
     return self.cache(fn(self)) 

    time_ago = self.time_ago 
    filename = self.filename 
    c_age = datetime.fromtimestamp(os.path.getctime(filename)) 
    m_age = datetime.fromtimestamp(os.path.getmtime(filename)) 
    print (c_age) 
    print (m_age) 
    print (time_ago) 
    if c_age < time_ago or m_age < time_ago: 
     return self.cache(fn(self)) 
    else: 
     return self.read() 

    def cache(self,data): 
     with open(self.filename,'r+') as ef: 
     ef.write(data) 
     return ef.read() 

    def read(self): 
    f = open(self.filename,'r') 
    data = f.read() 
    f.close() 
    return data 

我想打電話給在下面的類裝飾:

class Zabb(object): 

    @Cache('nodes.json',minutes=1) 
    def getNodes(self): 
    return "Get Nodes" 

我這樣稱呼它:

z = Zabb() 
nodes = z.getNodes() 

我得到以下錯誤:

Traceback (most recent call last): 
File "./deco.py", line 52, in <module> 
nodes = z.getNodes() 
TypeError: 'str' object is not callable 

我已經接近完成這項工作。我究竟做錯了什麼?

+3

'__call__'需要返回高階函數(包裹) –

回答

1

您需要從__call__方法返回一個高階函數(包裝)。添加一個內部方法並返回它。

def __call__(self, fn): 
    def wrapper(*args, **kwargs): # <-- Add this wrapper 
     if not os.path.isfile(self.filename): 
      return self.cache(fn(*args, **kwargs)) 

     time_ago = self.time_ago 
     filename = self.filename 
     c_age = datetime.fromtimestamp(os.path.getctime(filename)) 
     m_age = datetime.fromtimestamp(os.path.getmtime(filename)) 
     print (c_age) 
     print (m_age) 
     print (time_ago) 
     if c_age < time_ago or m_age < time_ago: 
      return self.cache(fn(*args, **kwargs)) 
     else: 
      return self.read() 
    return wrapper # <-- Return the wrapper 
1

裝飾是比較容易,如果你忘記了什麼是真正@deco語法糖和理性理解。在你的榜樣,

@Cache('nodes.json',minutes=1) 
def getNodes(self): 
    return "Get Nodes" 

真正含義是:

def getNodes(self): 
    return "Get Nodes" 

getNodes = Cache('nodes.json',minutes=1)(getNodes) 

這實際上重新綁定Zabb.getNodesCache('nodes.json',minutes=1).__call__(getNodes)結果 - 這是一個字符串,而不是功能。

你需要的是使Cache.__call__回報將包裝呼叫的裝飾功能的函數,即:

def __call__(self,fn): 
    def wrapper(*args, **kw): 
     if not os.path.isfile(self.filename): 
     return self.cache(fn(self)) 

     time_ago = self.time_ago 
     filename = self.filename 
     c_age = datetime.fromtimestamp(os.path.getctime(filename)) 
     m_age = datetime.fromtimestamp(os.path.getmtime(filename)) 
     print (c_age) 
     print (m_age) 
     print (time_ago) 
     if c_age < time_ago or m_age < time_ago: 
     return self.cache(fn(self)) 
     else: 
     return self.read() 
    return wrapper