Michele Simionato wrote:
On Sep 3, 12:19 am, Ethan Furman <et...@stoneleaf.us> wrote:

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)


What are you trying to do here? I miss why you don't use the usual
definition of factorial.
If you have a real life use case which is giving you trouble please
share. I do not see
why you want to pass a function to itself (?)

                            M. Simionato

Factorial is an example only.

The original thread by Bearophile:
  http://mail.python.org/pipermail/python-list/2009-May/711848.html

A similar thread from kj (where scopes is the actual problem):
  http://mail.python.org/pipermail/python-list/2009-August/725174.html

Basic summary: the recursive decorator (the one you snipped, not the one showing above), is a replacement for the bindfunc decorator, and preserves the signature of the decorated function, minus the self-referring first parameter. It solves kj's problem (although there are arguably better ways to do so), and I think helps with Bearophiles as well. As a bonus, the tracebacks are more accurate if the function is called incorrectly. Consider:

In [1]: def recursive(func):
   ...:     def wrapper(*args, **kwargs):
   ...:         return func(wrapper, *args, **kwargs)
   ...:     return wrapper
   ...:

In [2]: @recursive
   ...: def factorial(recurse, n):
   ...:     if n < 2:
   ...:         return 1
   ...:     else:
   ...:         return n * recurse(n-1)
   ...:

In [3]: factorial(1, 2)  # in incorrect call
--------------------------------------------------------------------
TypeError                          Traceback (most recent call last)

C:\pythonlib\<ipython console> in <module>()

C:\pythonlib\<ipython console> in wrapper(*args, **kwargs)

TypeError: factorial() takes exactly 2 arguments (3 given)
                                     ^^^^^^^^^^^^^^^^^^^^^
                   mildly confusing /

----------------------------------------
versus the error with the new decorator:
----------------------------------------

In [2]: factorial(4, 5)  # another incorrect call
--------------------------------------------------------------------
TypeError                          Traceback (most recent call last)

C:\pythonlib\<ipython console> in <module>()

TypeError: factorial() takes exactly 1 argument (2 given)
                                     ^^^^^^^^^^^^^^^^^^^^

This latter error matches how factorial actually should be called, both in normal code using it, and in the function itself, calling itself.

So that's the why and wherefore. Any comments on the new decorator itself? Here it is again:

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)

~Ethan~
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to