2017-01-01 99 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!