Python 访问在该装饰器外部的装饰器中创建的函数属性

Python 访问在该装饰器外部的装饰器中创建的函数属性,python,function,attributes,decorator,python-decorators,Python,Function,Attributes,Decorator,Python Decorators,我想计算一个给定函数被调用的次数 因此,我制作了一个countcalls装饰器,为我的函数赋予一个\ucallcount属性,该属性在每次调用时递增。很简单 我的问题是稍后将\u callcount值取回来 这是我的密码: import functools def countcalls(f): f.__callcount = 0 @functools.wraps(f) def _countcalls(*args, **kwds): f.__callco

我想计算一个给定函数被调用的次数

因此,我制作了一个
countcalls
装饰器,为我的函数赋予一个
\ucallcount
属性,该属性在每次调用时递增。很简单

我的问题是稍后将
\u callcount
值取回来

这是我的密码:

import functools

def countcalls(f):
    f.__callcount = 0

    @functools.wraps(f)
    def _countcalls(*args, **kwds):
        f.__callcount += 1
        print('  Called {0} time(s).'.format(f.__callcount))
        return f(*args, **kwds)
    return _countcalls

@countcalls
def fib(n):
    if n < 0:
        raise ValueError('n must be > 0')
    if n == 0 or n == 1:
        return 1

    return fib(n-1) + fib(n-2)

if __name__ == '__main__':
    print('Calling fib(3)...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('Calling fib(3) again...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('fib was called a total of {0} time(s).'.format(fib.__callcount)) 
为什么最后一行的fib.\u callcount等于
0
?如输出所示,
\uu callcount
将递增,并在调用
fib
之间保持不变

我错过了什么

f.__callcount = [0]

..........


f.__callcount[0] = f.__callcount[0] + 1

......


print('fib was called a total of {0} time(s).'.format(fib.__callcount[0]))
它起作用了。

也许有更像蟒蛇的东西

这正是你想要的。我在这里找到的-

类countcalls(对象):
“跟踪函数调用次数的装饰程序。”
__实例={}
定义初始化(self,f):
自
self.\uu numcalls=0
countcalls.\uuu实例[f]=self
self.\uuuuu doc\uuuuu=f.func\u doc
self.\uuuu name\uuuuu=f.func.func\u name
定义调用(self,*args,**kwargs):
自选电池+=1
返回自我(*args,**kwargs)
def计数(自身):
“返回调用函数f的次数。”
返回countcalls.\u实例[self.\u f]。\u numcalls
@静力学方法
def计数():
“为所有已注册函数返回{function:#of calls}的dict。”
返回countcalls中f的dict([(f.\u名称,countcalls.\u实例[f]。\u numcalls)]。\u实例])
@倒计时
def纤维(n):
如果n<0:
raise VALUERROR('n必须大于0')
如果n==0或n==1:
返回1
返回fib(n-1)+fib(n-2)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
打印('调用fib(3)…..)
x=fib(3)
打印('fib(3)={0}'。格式(x))
print('fib被调用的总时间为{0})。.format(fib.count())
打印('再次调用fib(3)…')
x=fib(3)
打印('fib(3)={0}'。格式(x))
print('fib被调用的总时间为{0})。.format(fib.count())

要添加属性的函数对象与“原始”函数不同。试试这个:

import functools

def countcalls(f):
    f.__callcount = 0

    @functools.wraps(f)
    def _countcalls(*args, **kwds):
        f.__callcount += 1
        print 'id(f):', id(f)
        print('  Called {0} time(s).'.format(f.__callcount))
        return f(*args, **kwds)
    return _countcalls


@countcalls
def fib(n):
    """fibinacci"""
    if n < 0:
        raise ValueError('n must be > 0')
    if n == 0 or n == 1:
        return 1

    return fib(n-1) + fib(n-2)


if __name__ == '__main__':
    print('Calling fib(3)...')
    x = fib(3)
    print 'id(fib):', id(fib)

"""
>>> 
Calling fib(3)...
id(f): 45611952
  Called 1 time(s).
id(f): 45611952
  Called 2 time(s).
id(f): 45611952
  Called 3 time(s).
id(f): 45611952
  Called 4 time(s).
id(f): 45611952
  Called 5 time(s).
id(fib): 45612016
>>>
"""
导入工具
def计数呼叫(f):
f、 \u\u callcount=0
@functools.wrapps(f)
def_countcalls(*args,**kwds):
f、 _uuucallcount+=1
打印“id(f):”,id(f)
打印('Called{0}时间。'.format(f.\u callcount))
返回f(*args,**kwds)
回电
@倒计时
def纤维(n):
“fibinaci”
如果n<0:
raise VALUERROR('n必须大于0')
如果n==0或n==1:
返回1
返回fib(n-1)+fib(n-2)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
打印('调用fib(3)…..)
x=fib(3)
打印“id(fib):”,id(fib)
"""
>>> 
呼叫fib(3)。。。
身份证号码(f):45611952
呼叫1次。
身份证号码(f):45611952
呼叫2次。
身份证号码(f):45611952
呼叫3次。
身份证号码(f):45611952
呼叫4次。
身份证号码(f):45611952
呼叫5次。
id(fib):4562016
>>>
"""

好吧,这是原因,经过一点帮助。谢谢大家

问题是函数是不可变的。e、 g

>>> def f(func):
...     return func()
...
>>> def g():
...     return 'sunflower seeds'
...
>>> id(g)
139636515497336
>>> g = f(g)
>>> id(g)
139636515515112
因此,获取函数
f
的唯一方法是在
countcalls
的定义中将
\uuu callcount
属性指定给
。但是我们已经返回了内部函数
\u countcalls
。我们可以同时返回
f
\u countcalls
,但这会弄乱
@countcalls
装饰器语法

你仍然可以这样做,只是没有那么漂亮

import functools

def countcalls(f):
    f.__callcount = 0

    @functools.wraps(f)
    def _countcalls(*args, **kwds):
        f.__callcount += 1
        print('  Called {0} time(s).'.format(f.__callcount))
        return f(*args, **kwds)
    return f, _countcalls

def fib(n):
    if n < 0:
        raise ValueError('n must be > 0')
    if n == 0 or n == 1:
        return 1

    return fib(n-1) + fib(n-2)

if __name__ == '__main__':
    counter, fib = countcalls(fib)

    print('Calling fib(3)...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('Calling fib(3) again...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('fib was called a total of {0} time(s).'.format(counter.__callcount))
导入工具
def计数呼叫(f):
f、 \u\u callcount=0
@functools.wrapps(f)
def_countcalls(*args,**kwds):
f、 _uuucallcount+=1
打印('Called{0}时间。'.format(f.\u callcount))
返回f(*args,**kwds)
返回f,\u countcalls
def纤维(n):
如果n<0:
raise VALUERROR('n必须大于0')
如果n==0或n==1:
返回1
返回fib(n-1)+fib(n-2)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
计数器,fib=countcalls(fib)
打印('调用fib(3)…..)
x=fib(3)
打印('fib(3)={0}'。格式(x))
打印('再次调用fib(3)…')
x=fib(3)
打印('fib(3)={0}'。格式(x))
print('fib被调用的总时间为{0})。.format(counter.\uu callcount))

长话短说,只需使用:D

是的,我的灵感来自于这段代码我想使用functools.wrapps和一个属性来保持简单,当我出于某种原因尝试向同一函数添加另一个装饰器时,countcalls装饰器给我带来了麻烦。在您的帮助下,我发布了一篇关于此问题的解释。这让我发疯了,谢谢你的帮助。将你的答案标记为答案,因为我仍然链接到这个示例。我认为这是一个范围/上下文问题。也许可以解释这种行为。好发现。我刚刚尝试使用
setattr(f,'.\u callcount',0)
getattr(f,'.\u callcount')
实现,但它们表现出与原始版本完全相同的行为。对于
f.\uu dict\uu['\uu callcount']
也有同样的行为。这肯定有效,这就是我想要的。但这很奇怪。。。如果您将
f.\uu callcount=[0]
更改为
f.\uu callcount={0:0}
,它也会起作用。为什么它适用于列表和字典,而不是整数?我将编辑我的问题以澄清。易变性可能与此有关-列表和目录是易变的。该列表在函数定义(装饰)时创建,类似于使用可变对象作为函数参数的默认值,它进入全局范围。
>>> def f(func):
...     return func()
...
>>> def g():
...     return 'sunflower seeds'
...
>>> id(g)
139636515497336
>>> g = f(g)
>>> id(g)
139636515515112
import functools

def countcalls(f):
    f.__callcount = 0

    @functools.wraps(f)
    def _countcalls(*args, **kwds):
        f.__callcount += 1
        print('  Called {0} time(s).'.format(f.__callcount))
        return f(*args, **kwds)
    return f, _countcalls

def fib(n):
    if n < 0:
        raise ValueError('n must be > 0')
    if n == 0 or n == 1:
        return 1

    return fib(n-1) + fib(n-2)

if __name__ == '__main__':
    counter, fib = countcalls(fib)

    print('Calling fib(3)...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('Calling fib(3) again...')
    x = fib(3)
    print('fib(3) = {0}'.format(x))

    print('fib was called a total of {0} time(s).'.format(counter.__callcount))