Unable to understand this python decorator

I am new to python decorators. I understood the basic concepts with simple examples. But when I tried to read this more practical decorator, I feel lost. Below is the code followed by my questions:

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

   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

@countcalls
def f():
   print 'f called'

f()
f()
f()
print f.count() # prints 3

My doubts:

  • When we prefix a decorator to a function, does this mean that we create the object of the decorator class exactly there? In our case, when it says:

    @countcalls def f(): print 'f called'

Is it @countcallsequivalent to creating an object countcallsand passing a function below to a method __init__?

  • __call__takes three arguments. selfok if this question is answered. What the hell are two other arguments: *args, **kwargsand what do they achieve?

  • How can I get better at decorators?

+5
source share
4

, , . .

class countcalls(object):

    def __init__(self, f):
        self._f = f
        self._numcalls = 0

    def __call__(self, *args, **kwargs):
        self._numcalls += 1
        return self._f(*args, **kwargs)

    def count(self):
        return self._numcalls

@countcalls
def f():
    print 'f called'

f()
f()
f()
print f.count() 

# output:
#   f called
#   f called
#   f called
#   3

@countcalls
def f():
    print 'f called'

- ,

def f():
    print 'f called'
f = countcalls(f)

, , _f.

f countcalls. f(...), f.__call__(...) - , () . , f, ?

    def __call__(self, *args, **kwargs):
        self._numcalls += 1
        return self._f(*args, **kwargs)

*args **kwargs, positional keyword dict, dict (. 4.7.4 ).

>>> def f(*args): print args
... 
>>> f(1, 2)
(1, 2)
>>> f()
()
>>> def add(a, b): return a + b
... 
>>> add(*[4, 3])
7
>>> add(**{'b': 5, 'a': 9})
14

def f(*args, **kwargs): return g(*args, **kwargs) .

, , __call__ ( f).

, @dec def f(...): ... def f(...): ... f = dec(f), , . , .

+5

, , ?

.

>>> @countcalls
... def f(): pass
>>> f
<a.countcalls object at 0x3175310>
>>> isinstance(f, countcalls)
True

, .

@countcalls countcalls init?

. , , ..

def f():
    print 'f called'
f = countcalls(f)

?

  • ,
  • PEP
+3
  • :

    f = countcalls(f)

    : , countcalls f .

  • These are function arguments. In your case, fit takes no arguments, but suppose it fis called as follows:

    f(3, 4, keyword=5)

    it *argswill contain 3 and 4, and **kwargswill contain a key / value pair keyword=5. For more information about *argsand **kwargssee this question .

  • Practice makes perfect.

+2
source
  • Yes, with the caveat that it also associates this object with the name specified in def.

  • Read about them, read examples, play with some.

0
source

All Articles