# call_counting.py
#
# ICS 33 Spring 2026
# Code Example
#
# This is a decorator named WithCallsCounted, which transforms a function into
# one that keeps track of how many times it's been called, but otherwise
# behaves like the original function.  The transformed function will have an
# additional count() method, which is how to ask it how many times it's been
# called.


class WithCallsCounted:
    '''
    A decorator that transforms a function into one that keeps track of how
    many times it's been called.  The resulting function will have an additional
    count() method that reports that number of calls.
    '''


    # Being a parameterless decorator (i.e., when we use it, we don't pass arguments
    # to it explicitly), the __init__ method will be called with the original
    # function passed as its only argument.  We'll store that function, as well
    # as initialize our count to zero, since the function hasn't been called yet.
    def __init__(self, func):
        self._func = func
        self._count = 0


    # When the decorated function is called, we'll increment the count, then
    # call the original function.  This order turns out to be important, because
    # we want the count to be incremented even if the function raises an
    # exception.  (A more complex version of this might separately count
    # successes and failures.)
    def __call__(self, *args, **kwargs):
        self._count += 1
        return self._func(*args, **kwargs)


    def count(self):
        'Returns the number of times the decorated function has been called'
        return self._count
