Python:装饰简单的递归函数

时间:2014-08-31 21:45:06

标签: python function recursion decorator

我想练习递归和装饰器并尝试做这个简单的功能,但它不起作用:

 def dec(func):

     def wrapper(number):
         print("Recursive count:")
         rec_cou(number)

     return wrapper

 @dec
 def rec_cou(number):
     """ Count from 0 to a given number from 50 and up """

     if number == 0:
         print(number)
         return number
     num = rec_cou(number - 1)
     print(num + 1)
     return num + 1

 rec_cou(53)

单独的递归函数效果很好,但是当我添加装饰器时会产生错误:超出最大递归深度

3 个答案:

答案 0 :(得分:2)

装饰者有两个问题:

  1. 您尝试调用修饰函数,在装饰器内再次有效地调用包装函数,因此您有一个无限的递归循环;请改为调用原始函数func
  2. 在外面,装饰函数应该像原始函数一样,特别是它应该返回它的结果;否则,您会在尝试添加数字和None
  3. 时遇到类型错误

    另外,目前你的装饰师没有计算任何东西......试试这个:

    def dec(func):
        func.count = 0          # give each decorated function its own counter
        def wrapper(number):
            print("Recursive count: %d" % func.count)
            func.count += 1     # increase counter
            return func(number) # call original function 'func' and return result
        return wrapper
    

    更新:从你的评论中,我似乎误解了你的装饰师应该做什么,你误解了装饰者的工作方式。第一次调用函数时,装饰器不会被调用一次,但它用装饰器中定义的函数替换函数。换句话说,

    @dec
    def foo(...):
        ...
    

    相当于

    def foo(...):
       ...
    foo = dec(foo)
    

    即。当装饰函数时,装饰器只调用一次,每次调用原始函数时调用装饰器中构造的函数,替换它。如果你只想打印一次,要么使用另一个答案中的装饰器,要么根本不使用装饰器:只需创建一个打印然后调用该函数的包装器。这对于为递归函数提供“入口点”并不罕见。

    def print_and_run(number):
        print("Recursive count:")
        rec_cou(number)
    

    BTW,这是我通常用于可视化递归调用的装饰器:

    def trace(f):
        trace.depth = 0
        def _f(*args, **kwargs):
            print "  " * trace.depth, ">", f.__name__, args, kwargs
            trace.depth += 1
            res = f(*args, **kwargs)
            trace.depth -= 1
            print "  " * trace.depth, "<", res
            return res
        return _f
    

答案 1 :(得分:2)

要解决最大递归深度问题,请调用传递给装饰器(func)而不是rec_cou的函数,并返回函数调用的值。也就是说,在第5行,将rec_cou(number)替换为return func(number)

编辑:

def decorate(function):
    def wrapper(parameter):
        if wrapper.initial:
            print("Recursive count:")
            wrapper.initial = False
        result = function(parameter)
        wrapper.initial = True
        return result

    wrapper.initial = True
    return wrapper

@decorate
def count(number):
    """ Prints integers on the interval [0, number] """
    if number:
        count(number - 1)
    print(number)

count(53)

没有装饰者:

def count(number):
    """ Prints integers on the interval [0, number] """
    if number:
        count(number - 1)
    else:
        print("Recursive count:")
    print(number)

count(53)

答案 2 :(得分:0)

如果您想要的是函数rec_cou在递归下降之前打印某些内容,只需修改该函数并且不要担心装饰器。

def rec_cou(number, internal_call=False):
     """ Count from 0 to a given number from 50 and up """
     if not internal_call:
         print "Now performing recursive count, starting with %d" % number
     if number == 0:
         return number
     num = rec_cou(number - 1, internal_call=True)
     return num + 1

正如我在评论中提到的,我所做的就是接受乔尔答案背后的想法(这是为了添加变量 - 我称之为&#34;标志&#34; - - 指示函数是在外部调用还是作为递归的一部分调用)并在函数本身内移动了标志变量(我称之为internal_call,而Joel称之为initial)。


此外,我不确定所有这些num业务是关于什么的。请注意:

  • 对于0案例,rec_cou返回0。
  • 对于number > 0num设置为rec_cou(number-1)返回的值,然后返回1+num

例如,在rec_cou(1)的情况下,num设置为rec_cou(0),即0,则返回0 + 1,即1 {1}}。同样,对于rec_cou(2),返回的值超过rec_cou(1)的值,因此会返回2

简而言之,对于每个自然数,rec_cou(number)都会返回输入数字的值。它不清楚你想要实现的目标,但你所获得的是身份功能,这似乎不太可能是你想要的。