繁体   English   中英

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

[英]Accessing function attribute created in a decorator outside that decorator

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

因此,我做了一个countcalls装饰器,为我的函数提供__callcount属性,该属性在每次调用时都会递增。 很简单。

我的问题是稍后收回__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)) 

生成以下输出(Python v3.3.0):

Calling fib(3)...
  Called 1 time(s).
  Called 2 time(s).
  Called 3 time(s).
  Called 4 time(s).
  Called 5 time(s).
fib(3) = 3
Calling fib(3) again...
  Called 6 time(s).
  Called 7 time(s).
  Called 8 time(s).
  Called 9 time(s).
  Called 10 time(s).
fib(3) = 3
fib was called a total of 0 time(s).

为什么fib.__callcount在最后一行等于0 如输出所示, __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]))

有用。
也许还有一些Pythonic

这就是您想要的。 我在这里-https : //wiki.python.org/moin/PythonDecoratorLibrary#Alternate_Counting_function_calls

class countcalls(object):
   "Decorator that keeps track of the number of times a function is called."

   __instances = {}

   def __init__(self, f):
      self.__f = f
      self.__numcalls = 0
      countcalls.__instances[f] = self
      self.__doc__ = f.func_doc
      self.__name__ = f.func.func_name

   def __call__(self, *args, **kwargs):
      self.__numcalls += 1
      return self.__f(*args, **kwargs)

   def count(self):
      "Return the number of times the function f was called."
      return countcalls.__instances[self.__f].__numcalls

   @staticmethod
   def counts():
      "Return a dict of {function: # of calls} for all registered functions."
      return dict([(f.__name__, countcalls.__instances[f].__numcalls) for f in countcalls.__instances])

@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('fib was called a total of {0} time(s).'.format(fib.count())) 

    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.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(func):
...     return func()
...
>>> def g():
...     return 'sunflower seeds'
...
>>> id(g)
139636515497336
>>> g = f(g)
>>> id(g)
139636515515112

因此,要获得该功能的唯一途径f我们分配__callcount中的定义属性countcalls是从返回功能callcount 但是我们已经在返回内部函数_countcalls 我们可以同时返回f_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))

长话短说,只需使用Python Decorator Library中 :D

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM