我在使用'post hook'函数装饰本机列表方法时遇到问题。我需要一个装饰器,它将本地列表方法作为参数,并且在调用该方法之后,它还应该运行正在装饰的函数代码。 这段代码可以正常工作:
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 :(得分: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]
答案 1 :(得分:0)
感谢@ martijn-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!