E
Ethan Furman
Greetings, List!
The recent thread about a recursive function in a class definition led
me back to a post about bindfunc from Arnaud, and from there I found
Michele Simionato's decorator module (many thanks! , and from there I
began to wonder...
from decorator import decorator
@decorator
def recursive1(func, *args, **kwargs):
return func(func, *args, **kwargs)
@recursive1
def factorial1(recurse, n):
if n < 2:
return 1
return n * recurse(n-1)
factorial(4)
TypeError: factorial1() takes exactly 2 arguments (1 given)
Now it's *entirely* possible that I am missing something easy here, but
at any rate I was thinking about the above error, about how one of the
design goals behind the decorator module was to enable introspection to
give accurate information about usage, etc, and how if the signature was
*exactly* preserved then we have this pesky and seemingly out of place
variable, not mention we can't 'just use it' in the code.
So I set out to create a signature-modifying decorator that would lose
that first parameter from the wrapper, while keeping the rest of the
signature intact. This is what I was able to come up with:
def recursive(func):
"""
recursive is a signature modifying decorator.
Specifially, it removes the first argument, so
that calls to the decorated function act as
if it does not exist.
"""
argspec = inspect.getargspec(func)
first_arg = argspec[0][0]
newargspec = (argspec[0][1:], ) + argspec[1:]
fi = decorator.FunctionMaker(func)
old_sig = inspect.formatargspec( \
formatvalue=lambda val: "", *argspec)[1:-1]
new_sig = inspect.formatargspec( \
formatvalue=lambda val: "", *newargspec)[1:-1]
new_def = '%s(%s)' % (fi.name, new_sig)
body = 'return func(%s)' % old_sig
def wrapper(*newargspec):
return func(wrapper, *newargspec)
evaldict = {'func':func, first_arg:wrapper}
return fi.create(new_def, body, evaldict, \
doc=fi.doc, module=fi.module)
I cannot remember whose sig it is at the moment (maybe Aahz'?), but it
says something about debugging being twice as hard as coding, so if you
code as cleverly as you can you are, by definition, not smart enough to
debug it! And considering the amount of trial-and-error that went into
this (along with some thought and reasoning about scopes !-), I am
hoping to get some code review.
And if there's an easier way to do this, I wouldn't mind knowing about
that, too!
Many thanks.
~Ethan~
The recent thread about a recursive function in a class definition led
me back to a post about bindfunc from Arnaud, and from there I found
Michele Simionato's decorator module (many thanks! , and from there I
began to wonder...
from decorator import decorator
@decorator
def recursive1(func, *args, **kwargs):
return func(func, *args, **kwargs)
@recursive1
def factorial1(recurse, n):
if n < 2:
return 1
return n * recurse(n-1)
factorial(4)
TypeError: factorial1() takes exactly 2 arguments (1 given)
Now it's *entirely* possible that I am missing something easy here, but
at any rate I was thinking about the above error, about how one of the
design goals behind the decorator module was to enable introspection to
give accurate information about usage, etc, and how if the signature was
*exactly* preserved then we have this pesky and seemingly out of place
variable, not mention we can't 'just use it' in the code.
So I set out to create a signature-modifying decorator that would lose
that first parameter from the wrapper, while keeping the rest of the
signature intact. This is what I was able to come up with:
def recursive(func):
"""
recursive is a signature modifying decorator.
Specifially, it removes the first argument, so
that calls to the decorated function act as
if it does not exist.
"""
argspec = inspect.getargspec(func)
first_arg = argspec[0][0]
newargspec = (argspec[0][1:], ) + argspec[1:]
fi = decorator.FunctionMaker(func)
old_sig = inspect.formatargspec( \
formatvalue=lambda val: "", *argspec)[1:-1]
new_sig = inspect.formatargspec( \
formatvalue=lambda val: "", *newargspec)[1:-1]
new_def = '%s(%s)' % (fi.name, new_sig)
body = 'return func(%s)' % old_sig
def wrapper(*newargspec):
return func(wrapper, *newargspec)
evaldict = {'func':func, first_arg:wrapper}
return fi.create(new_def, body, evaldict, \
doc=fi.doc, module=fi.module)
I cannot remember whose sig it is at the moment (maybe Aahz'?), but it
says something about debugging being twice as hard as coding, so if you
code as cleverly as you can you are, by definition, not smart enough to
debug it! And considering the amount of trial-and-error that went into
this (along with some thought and reasoning about scopes !-), I am
hoping to get some code review.
And if there's an easier way to do this, I wouldn't mind knowing about
that, too!
Many thanks.
~Ethan~