函数的属性

时间:2018-03-27 15:28:22

标签: python python-3.x function attributes decorator

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一样,但是它是吗?

2 个答案:

答案 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被明确地取消引用,因为它不再在范围内,只有返回代码对象。