Форум сайта python.su
0
имеем 2 аналогичных декоратора - первый реализован функцией, второй - классом:
class Decorator:
def __init__(self, method):
self.method = method
def __call__(self, *args):
print 'decorator-class arguments: ', args
return self.method(*args)
def decorator(method):
def inner(*args):
print 'decorator-function arguments: ', args
return method(*args)
return inner
class My:
def test(self, i):
return i+1
obj = My()
print obj.test(100)
decorator-function arguments: (<__main__.My instance at 0x01D1D878>, 100)
101
decorator-class arguments: (100,)
Traceback (most recent call last):
File "C:\Users\dima\Documents\NetBeansProjects\db\src\decorators.py", line 42, in <module>
print m.test(100)
File "C:\Users\dima\Documents\NetBeansProjects\db\src\decorators.py", line 9, in __call__
return self.method(*args)
TypeError: test() takes exactly 2 arguments (1 given)
Отредактировано (Авг. 10, 2009 00:15:27)
Офлайн
2
dimabestНет.
это баг?
dimabestВ первом случае после декорирования функция inner из декоратора становиться unbound методом test класса My. После инстанцирования этого класса, метод становиться bound, т.е. по сути появляется его карринг с объектом My в виде параметра self.
Почему?
Офлайн
0
Daevaorn
Точно.
А как обойти такое поведение?
Я придумал решение - если декоратор принимает параметр - то имеем не 2, а 3 уровня вложенности и метод __call__ принимает такой же вид как функция decorator и первого поста:
class Decorator:
def __init__(self, param=None):
pass
def __call__(self, method):
def inner(*args):
print 'decorator-class arguments: ', args
return method(*args)
return inner
decorator-class arguments: (<__main__.My instance at 0x01E1D940>, 100)
101
class My:
@Decorator()
def test(self, i):
return i+1
Отредактировано (Авг. 10, 2009 01:36:34)
Офлайн
14
class Parametrized(object):
"""
A baseclass that allows to defer instance creation until
it gets a non-keyword argument (specifying the rule).
For example here's what it does to `compute`. A trivial case:
>>> compute(lambda:None)
compute(None)
`compute` can also take additional arguments:
>>> compute(lambda:None, resetting_to=0)
compute(None)
But we want to be able to use it as a decorator and still pass
additional arguments, so the following should work:
>>> compute(resetting_to=0)(lambda:None)
compute(None)
It worked because the first call was intercepted in
Parametrized.__new__ and returned the lambda that acts as a
real decorator:
>>> compute(resetting_to=0)
<function <lambda> at 0x00C343B0>
"""
def __new__(cls, *args, **kw):
if not args:
return lambda _: cls(_, **kw)
return object.__new__(cls)
Офлайн
0
Спасибо, работает. Правда, я пока не понял как :)
Офлайн
14
Подумай несколько раз, перечитай док-стринг…
Я тоже сразу не понял, и специально для меня-балбеса автор его такой длинный написал :)
Офлайн