Python 函数的属性 def fib(n): 如果n>>fib=计数(fib) >>>fib(5) 5. >>>小谎,呼叫计数 15 >>>计数 命名错误

Python 函数的属性 def fib(n): 如果n>>fib=计数(fib) >>>fib(5) 5. >>>小谎,呼叫计数 15 >>>计数 命名错误,python,python-3.x,function,attributes,decorator,Python,Python 3.x,Function,Attributes,Decorator,据我所知,fib现在实际上是计数的。然而,我不明白为什么,当我想调用counted.call\u count时,我必须调用fib.call\u count 我认为counted是类函数的一个实例。但是什么是计数。呼叫计数的确切含义是什么 更具体地说,我不理解在代码类****:之外定义的属性。在这种情况下,定义属性就像self.call\u count更清晰,因为我可以理解self来引用实例。在我的例子中,似乎counted.call\u count中的counted就像self.call\u c

据我所知,
fib
现在实际上是
计数的
。然而,我不明白为什么,当我想调用
counted.call\u count
时,我必须调用
fib.call\u count


我认为
counted
是类
函数的一个实例。但是什么是计数。呼叫计数的确切含义是什么


更具体地说,我不理解在代码
类****:
之外定义的属性。在这种情况下,定义属性就像
self.call\u count
更清晰,因为我可以理解
self
来引用实例。在我的例子中,似乎
counted.call\u count
中的
counted
就像
self.call\u count
中的
self
一样,但是它是吗?

这里的问题是在主程序代码中没有调用
counted
函数,它被称为
fib
。(
count
返回
counted
,通过赋值绑定到
fib
),因此您应该使用这个名称

您对修饰函数名的依赖有点脆弱。你会发现你依赖这个名字。如果您将调用代码更改为

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
您将看到它只计算外部调用

你实际上是在写一个装饰师,尽管你可能不知道。写这篇文章的更传统的方法可能是

fibfun = count(fib)
fibfun(5)
fibfun.call_count
问题是,您只能调用此修饰函数一次,除非您考虑到随着进一步调用,计数会继续累积:

>>> fib(5)
5
>>> fib.call_count
15

这是可以接受的。否则,可能需要进一步的复杂性。

这里的问题是,
counted
函数在主程序代码中没有调用
counted
,它被称为
fib
。(
count
返回
counted
,通过赋值绑定到
fib
),因此您应该使用这个名称

您对修饰函数名的依赖有点脆弱。你会发现你依赖这个名字。如果您将调用代码更改为

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
您将看到它只计算外部调用

你实际上是在写一个装饰师,尽管你可能不知道。写这篇文章的更传统的方法可能是

fibfun = count(fib)
fibfun(5)
fibfun.call_count
问题是,您只能调用此修饰函数一次,除非您考虑到随着进一步调用,计数会继续累积:

>>> fib(5)
5
>>> fib.call_count
15

这是可以接受的。否则,可能需要进一步的复杂性。

思考python解释器在幕后是如何工作的,这可能有助于您理解这里发生了什么。当源代码通过lexer并转换为字节码时,我们可以看到对“load”和“store”命令的引用,这表明解释器主要作为基于堆栈的机器工作:

>>> fib(5)
5
>>> fib.call_count
30
>dis.dis(计数)
2 0负载\u闭合0(计数)
2负载关闭1(f)
4构建元组2
6加载常数1(<0x00000193D034C8A0处计数的代码对象,文件“ipython-input-2-a31b910d61de”,第2行>)
8负载常数2('count..counted')
10制作功能8
12门店编号0(已计算)
5 14荷载常数3(0)
16加载顺序为0(计数)
18存储属性0(呼叫计数)
6 20加载顺序0(计数)
22返回值

我们还可以看到,
call\u count
属性绑定到创建的新代码对象,但是名称:
counted
被显式取消引用,因为它不再在作用域中,只返回code对象。

思考python解释器如何在后台工作可能有助于您理解这里发生的事情。当源代码通过lexer并转换为字节码时,我们可以看到对“load”和“store”命令的引用,这表明解释器主要作为基于堆栈的机器工作:

>>> fib(5)
5
>>> fib.call_count
30
>dis.dis(计数)
2 0负载\u闭合0(计数)
2负载关闭1(f)
4构建元组2
6加载常数1(<0x00000193D034C8A0处计数的代码对象,文件“ipython-input-2-a31b910d61de”,第2行>)
8负载常数2('count..counted')
10制作功能8
12门店编号0(已计算)
5 14荷载常数3(0)
16加载顺序为0(计数)
18存储属性0(呼叫计数)
6 20加载顺序0(计数)
22返回值

我们还可以看到,
call\u count
属性绑定到创建的新代码对象,但是名称:
counted
被显式取消引用,因为它不再在范围内,只返回代码对象。

counted。call\u count
不会只显示“Error”。它将显示NameError,告诉您当前范围中未定义
计数的
。@DanielRoseman:好的,谢谢!但我认为这不是重点。。。我想知道的是,为什么我要叫“fib.call\u count”,正如我刚才所说的那样。无论如何,谢谢<代码>计数。调用\u计数
不会只显示“错误”。它将显示NameError,告诉您当前范围中未定义
计数的
。@DanielRoseman:好的,谢谢!但我认为这不是重点。。。我想知道的是,为什么我要叫“fib.call\u count”,正如我刚才所说的那样。无论如何,谢谢!