Форум сайта python.su
имеем 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)
Офлайн
dimabestНет.
это баг?
dimabestВ первом случае после декорирования функция inner из декоратора становиться unbound методом test класса My. После инстанцирования этого класса, метод становиться bound, т.е. по сути появляется его карринг с объектом My в виде параметра self.
Почему?
Офлайн
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)
Офлайн
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)
Офлайн
Спасибо, работает. Правда, я пока не понял как :)
Офлайн
Подумай несколько раз, перечитай док-стринг…
Я тоже сразу не понял, и специально для меня-балбеса автор его такой длинный написал :)
Офлайн