text: """Decorators
  Recall the simple closure example we did which allowed us to maintain a count of ho9w many times a function was called:
  
  
  
  def counter(fn):
      count = 0
      def inner(*args, **kwargs):    # using *args. **kwargs means we can call any function fn with any combination of positional and keyword arguments
          nonlocal count
          count += 1
          print('Function {0} was called {1} times'.format(fn.__name__, count))
          return fn(*args, **kwargs)
      return inner
  
  def add(a, b=0):
      return a + b
  
  add = counter(add)
  
  result = add(1, 2)  # Function add was called 1 times
                      # result = 3
  
  print(result)
  
  I essentially modified our add function by wrapping it inside another function that added some functionally to it
  
  I can also say that we decorated ourfunction add with the function counter
  
  And I call counter a decorator function
  
  
  In general a decorator function:
  takes a function as an argument
  returns a closure
  the closure usually accepts any combination of parameters
  runs some code in the inner function(closure)
  
  """