为什么这两种方法会产生不同的结果?

时间:2018-11-15 21:00:06

标签: python scope nested decorator nested-function

def once(fcn):
        func = [fcn]
        def inner(*args):
            return func.pop()(*args) if len(func) else None
        return inner


def add(a,b):
    return a+b


oneAddition = once(add)
print(oneAddition(2,2)) # 4
print(oneAddition(2,2)) # None
print(oneAddition(12,200)) # None

print(once(add)(2,2)) # 4
print(once(add)(2,2)) # Should return None, returns 4
print(once(add)(12,200)) # Should return None, returns 212

因此,此嵌套函数的目的是跟踪调用外部函数的次数。它仅在首次调用时返回添加结果。之后,无论何时调用,它都将返回None。

真正引起我兴趣的是oneAddition = once(add)-> oneAddition(2,2)和一次(add)(x,y)的行为不同。

在第二种方法中,似乎也执行了外部函数。使用第一种方法,外部函数仅在构造时执行(非常类似于装饰器)。

有人可以向我解释为什么吗?非常感谢。

P.S。我知道使用非本地var将是一个更合适的解决方案,我只是加入了 list-in-a-list 方法,因为它看起来很酷(可以在线找到)。

1 个答案:

答案 0 :(得分:2)

每次once(add)调用都会创建一个新的inner闭包,并带有一个新的func引用,并将其返回。因此,您后三个print中的每一个都在完全独立的func列表上进行操作,从而输出。

一种简单的查看方法是查看字节码:

>>> dis(once)
  2           0 LOAD_FAST                0 (fcn)
              2 BUILD_LIST               1
              4 STORE_DEREF              0 (func)

  3           6 LOAD_CLOSURE             0 (func)
              8 BUILD_TUPLE              1
             10 LOAD_CONST               1 (<code object inner at 0x10c64c300, file "<stdin>", line 3>)
             12 LOAD_CONST               2 ('once.<locals>.inner')
             14 MAKE_FUNCTION            8
             16 STORE_FAST               1 (inner)

  5          18 LOAD_FAST                1 (inner)
             20 RETURN_VALUE

请注意,once在每次调用(func)上都会建立一个 new BUILD_LIST列表,而该列表又被返回的闭包使用({ {1}}。

另一种查看方法是在MAKE_FUNCTION内打印id(func)

inner()

请注意,这些ID不同。