【问题标题】:Why does decorating a recursive function yield unexpected flow of results?为什么装饰递归函数会产生意想不到的结果流?
【发布时间】:2020-10-17 16:17:43
【问题描述】:
# THE CODE 

import time 

# A decorator to time the function being exeuted
def timeit(func):
    # The Wrapper
    def timer(arg):
        current_time = time.time()
        result = func(arg)
        print("result = ",result) # I know this hinders the time calculation part ... just for testing
        time_difference = time.time() - current_time
        return result 
    return timer

# Recursive Function to find factorial
@timeit
def factorial(n):
    print("I'm inside factorial")
    return n if n < 2 else n * factorial(n-1)

if __name__ == "__main__":
    print(factorial(5))
-----output------

I'm inside factorial
I'm inside factorial
I'm inside factorial
I'm inside factorial
I'm inside factorial
result =  1
result =  2
result =  6
result =  24
result =  120
120

我编写了这个程序来计时我的阶乘函数,函数的结果符合预期,但程序的流程不是我预期的。我想了解装饰器和递归是如何工作的,即递归如何映射到原始函数而不是装饰函数,以及如何在不打印“我在阶乘中”的情况下连续打印结果,如在最后的输出中所见。非常感谢您解释流程或指向正确的资源。

【问题讨论】:

    标签: python recursion decorator


    【解决方案1】:

    这就是我所期望的程序流程。

    def factorial(n):
        print("I'm inside factorial")
        return n if n < 2 else n * factorial(n-1)
    

    在返回任何允许控制权返回装饰器之前再次调用factorial 函数。这就是递归的工作原理。它建立调用堆栈,直到达到结束条件。然后它以相反的顺序返回它们。

    在这种情况下,程序调用factorial(5)。由于 5 不 factorial(4)。 4 不是 factorial(3),然后触发factorial(2),然后触发factorial(1)。此时 n factorial(2) 调用,在该调用中它将 2 乘以从 @987654329 返回的结果@ 将 2 返回给装饰器。这一直持续到factorial(3) = 6、factorial(4) = 24,最后到factorial(5) = 120

    【讨论】:

      【解决方案2】:

      您的递归是在每个级别上调用包装函数。也就是调用栈是这样的:

      timer(5)
        factorial(5)
          timer(4)
            factorial(4)
              timer(3)
                factorial(3)
                  ...
      

      这是因为装饰器语法将全局命名空间中的原始 factorial 函数替换为装饰器返回的 timer 函数。 timer 函数仍然可以调用原始 factorial 函数,因为它可以通过装饰器函数命名空间中的 func 变量访问对它的引用(这意味着 timer 是一个闭包)。

      您首先会看到所有 "I'm inside" 消息,因为它们是在每次调用 factorial 之前打印出来的。 "result =" 消息在最后以相反的顺序打印出来,因为它们在 timer 内每次调用 func 之后,因此它们在堆栈展开时发生,而不是在堆栈建立时发生。

      这是一个修改后的调用堆栈,也显示了 print 调用:

      timer(5)
        factorial(5)
          print("I'm inside factorial")
          timer(4)
            factorial(4)
              print("I'm inside factorial")
              timer(3)
                factorial(3)
                  ...
                print("result = ", 6)
            print("result = ", 24)
        print("result = ", 120)
      

      在调用一个或两个函数之前和之后打印输出可能会澄清一些事情:

      def timeit(func):
          # The Wrapper
          def timer(arg):
              print("timer start")                                  # new output here
              current_time = time.time()
              result = func(arg)
              print("result = ",result)
              time_difference = time.time() - current_time
              return result
          return timer
      
      # Recursive Function to find factorial
      @timeit
      def factorial(n):
          print("I'm inside factorial")
          result = n if n < 2 else n * factorial(n-1)
          print("Factorial computed")                               # and here
          return result
      

      【讨论】:

        【解决方案3】:

        Python 在运行时不知道函数factorial 是什么,而是知道它在内存中的位置。当你装饰它时,你正在覆盖函数,所以每当 python 调用它时,“原始版本”不再存在。你甚至可以在调用递归函数时更改它的定义,问题是在每次调用时,python 都会定位函数在内存中的位置并读取当前版本以执行它。

        【讨论】:

        • 但是不会'Im inside factorial'并且结果= ..被交替打印而不是一个接一个地打印。我的意思是为什么一半的函数运行了 n 次,但是即使在递归结束后,结果仍然会一一打印其所有原始值?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-11
        • 2021-10-04
        • 1970-01-01
        • 2011-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多