def fib(n):
if n <= 1:
return n
else:
return fib(n - 2) + fib(n - 1)
def count(f):
def counted(*args):
counted.call_count += 1
return f(*args)
counted.call_count = 0
return counted
>>> fib = count(fib)
>>> fib(5)
5
>>> fib.call_count
15
>>> counted.call_count
NameError
我了解fib
现在实际上是counted
。但是,我无法弄清楚为什么,当我想致电counted.call_count
时,我必须致电fib.call_count
。
我认为counted
是类Function
的一个实例。但是counted.call_count
究竟意味着什么?
更具体地说,我不理解在代码class ****:
之外定义的属性。在这种情况下,定义属性就像self.call_count
更清楚,因为我可以理解self
来引用该实例。在我的示例中,counted
中的counted.call_count
似乎与self
中的self.call_count
一样,但是它是吗?
答案 0 :(得分:2)
这里的问题是mydata[ , 3:1]
函数在主程序代码中没有被称为counted
,它被称为counted
。 (fib
返回count
,您将counted
与作业绑定在一起),这样就是您应该使用的名称。
您对装饰功能名称的依赖有点脆弱。你会发现你依赖那个名字。如果您将调用代码更改为
fib
你会看到它只计算外线电话。
你实际上在写一个装饰师,虽然你可能不知道。一种更传统的写入方式可能是
fibfun = count(fib)
fibfun(5)
fibfun.call_count
然后没有必要手动包装函数(装饰器语法只是为你调用),你可以写
def count(f):
def counted(*args):
counted.call_count += 1
return f(*args)
counted.call_count = 0
return counted
@count
def fib(n):
if n <= 1:
return n
else:
return fib(n - 2) + fib(n - 1)
问题是你只能调用一次这个修饰函数,除非你考虑到进一步调用时计数会继续累积:
>>> fib(5)
5
>>> fib.call_count
15
这可能是可以接受的。否则,可能需要进一步的复杂性。
答案 1 :(得分:1)
考虑python解释器如何在幕后工作可能有助于您了解这里发生了什么。当源代码通过词法分析器并转换为字节码时,我们可以看到对“load”和“store”命令的引用,指示解释器主要作为基于堆栈的机器工作:
>>> dis.dis(count)
2 0 LOAD_CLOSURE 0 (counted)
2 LOAD_CLOSURE 1 (f)
4 BUILD_TUPLE 2
6 LOAD_CONST 1 (<code object counted at 0x00000193D034C8A0, file "<ipython-input-2-a31b910d61de>", line 2>)
8 LOAD_CONST 2 ('count.<locals>.counted')
10 MAKE_FUNCTION 8
12 STORE_DEREF 0 (counted)
5 14 LOAD_CONST 3 (0)
16 LOAD_DEREF 0 (counted)
18 STORE_ATTR 0 (call_count)
6 20 LOAD_DEREF 0 (counted)
22 RETURN_VALUE
我们还可以看到call_count
属性绑定到创建的新代码对象,但名称:counted
被明确地取消引用,因为它不再在范围内,只有返回代码对象。