2017-01-01 182 views
1

我用'post hook'函數裝飾本機列表方法時遇到問題。我需要一個裝飾器,它將一個本地列表方法作爲參數,在調用方法之後,它應該運行一個正在裝飾的函數代碼。 這段代碼工作好:python2中的類方法鉤裝飾器

def _add_hook(clsmtd, info_fcn): 
    def hooked(s, *p, **k): 
     ret = clsmtd(s, *p, **k) 
     '''some magic happens here''' 
     print info_fcn(s, *p, **k) 
     return ret 
    return hooked 

def reporting_list_deco(cls): 
    def append_info(s, *p, **k): 
     return 'append to %s' % s._name 
    cls.append = _add_hook(cls.append, append_info) 

    def remove_info(s, idx, *p, **k): 
     return 'removin %s[%s]' % (s._name, idx) 
    cls.remove = _add_hook(cls.remove, remove_info) 

    def setitem_info(s, idx, *p, **k): 
     return 'setitem %s[%s]' % (s._name, idx) 
    cls.__setitem__ = _add_hook(cls.__setitem__, setitem_info) 
    ''' and so on also for pop, sort, insert, reverse and extend ''' 
    return cls 

def test_reporting_list(): 
    @reporting_list_deco 
    class reportin_list(list): 
     def __init__(self, *p, **k): 
      super(reportin_list, self).__init__(*p, **k) 
      self._name = 'foo' 
    rl = reportin_list([6,6,6]) 
    rl[1] = 1 
    rl.append(7) 
    rl.remove(1) 
    ''' outputs: 
     setitem foo[1] 
     append to foo 
     removin foo[1] 
    ''' 

但我想它寫這樣的:

def better_reporting_list_deco(cls): 
    @_add_hook_for(cls.append) 
    def append_info(s, *p, **k): 
     return 'append to %s' % s._name 

    @_add_hook_for(cls.remove) 
    def remove_info(s, idx, *p, **k): 
     return 'removin %s[%s]' % (s._name, idx) 

    @_add_hook_for(cls.__setitem__) 
    def setitem_info(s, idx, *p, **k): 
     return 'setitem %s[%s]' % (s._name, idx) 

    return cls 

的問題是,我不知道該怎麼寫_add_hook_for裝飾。請指教。

+0

你也許可以這樣做:@_add_hook_for(cls,'append')',這看起來像是一個裝飾者的奇怪用法。 – jonrsharpe

+0

@jonrsharpe:好點。但幾乎每個本地方法都有不同的接口,報告功能需要不同的參數。 – Mikaelblomkvistsson

+0

這是否總是適用於內置類型?然後,我可以使用Python 2和3的相同代碼工作:-) –

回答

3

寫一個裝飾工廠。你做要通過類,因爲你不能可靠地檢索,否則(你正在傳遞父類類的方法,所以即使我們設法檢索上下文,我們會使用錯誤的目標類):

def _add_hook_for(cls, target): 
    def hook_decorator(hook): 
     def hooked(s, *p, **k): 
      ret = target(s, *p, **k) 
      # some magic happens here 
      print hook(s, *p, **k) 
      return ret 
     setattr(cls, target.__name__, hooked) 
     return hook 
    return hook_decorator 

您的類裝飾就變成了:

def better_reporting_list_deco(cls): 
    @_add_hook_for(cls, cls.append) 
    def append_info(s, *p, **k): 
     return 'append to %s' % s._name 

    @_add_hook_for(cls, cls.remove) 
    def remove_info(s, idx, *p, **k): 
     return 'removing %s[%s]' % (s._name, idx) 

    @_add_hook_for(cls, cls.__setitem__) 
    def setitem_info(s, idx, *p, **k): 
     return 'setitem %s[%s]' % (s._name, idx) 

    return cls 

演示:

>>> @better_reporting_list_deco 
... class reporting_list(list): 
...  def __init__(self, *p, **k): 
...   super(reporting_list, self).__init__(*p, **k) 
...   self._name = 'foo' 
... 
>>> rl = reporting_list([6, 6, 6]) 
>>> rl[1] = 1 
setitem foo[1] 
>>> rl.append(7) 
append to foo 
>>> rl.remove(1) 
removing foo[1] 
0

感謝@馬亭,PIETERS我重寫了它這樣的(效果很好在Python 2.7):

def _add_hook_for(cls, target): 
    def hook_decorator(hook): 
     def hooked(s, *p, **k): 
      ret = target(s, *p, **k) 
      # some magic happens here 
      print(hook(s, target.__name__, *p, **k)) 
      return ret 
     setattr(cls, target.__name__, hooked) 
     return hook 
    return hook_decorator 

def reporting_list_deco(cls): 
    @_add_hook_for(cls, cls.append) 
    @_add_hook_for(cls, cls.extend) 
    @_add_hook_for(cls, cls.sort) 
    @_add_hook_for(cls, cls.reverse) 
    def no_idx_info(s, op, *p, **k): 
     return '%s %s' % (op, s._name) 

    @_add_hook_for(cls, cls.__setitem__) 
    @_add_hook_for(cls, cls.insert) 
    @_add_hook_for(cls, cls.__delitem__) 
    @_add_hook_for(cls, cls.remove) 
    @_add_hook_for(cls, cls.pop) 
    def idx_info(s, op, idx='', *p, **k): 
     return '%s %s[%s]' % (op, s._name, idx) 
    return cls 

def test_reporting_list(): 
    @reporting_list_deco 
    class reporting_list(list): 
     def __init__(self, name, *p, **k): 
      super(reporting_list, self).__init__(*p, **k) 
      self._name = name 

    rl = reporting_list('foo', [61,62,63]) 
    rl[1] = 1 
    rl.append(7) 
    rl.remove(1) 
    rl.extend([5,6,7]) 
    rl.pop() 
    rl.pop(2) 
    rl.sort() 
    del rl[0] 
    rl.reverse() 
    print(rl) 

輸出:

__setitem__ foo[1] 
append foo 
remove foo[1] 
extend foo 
pop foo[] 
pop foo[2] 
sort foo 
__delitem__ foo[0] 
reverse foo 
[63, 61, 6] 

它也可以應用到字典。謝謝Martijn!