very-cool-group

decorators

A decorator is a function that modifies another function.

Consider the following example of a timer decorator:

>>> import time
>>> def timer(f):
...     def inner(*args, **kwargs):
...         start = time.time()
...         result = f(*args, **kwargs)
...         print('Time elapsed:', time.time() - start)
...         return result
...     return inner
...
>>> @timer
... def slow(delay=1):
...     time.sleep(delay)
...     return 'Finished!'
...
>>> print(slow())
Time elapsed: 1.0011568069458008
Finished!
>>> print(slow(3))
Time elapsed: 3.000307321548462
Finished!

More information:
- Corey Schafer's video on decorators
- Real python article

functions-are-objects

When assigning a new name to a function, storing it in a container, or passing it as an argument, a common mistake made is to call the function. Instead of getting the actual function, you'll get its return value.

In Python you can treat function names just like any other variable. Assume there was a function called now that returns the current time. If you did x = now(), the current time would be assigned to x, but if you did x = now, the function now itself would be assigned to x. x and now would both equally reference the function.

Examples

# assigning new name

def foo():
    return 'bar'

def spam():
    return 'eggs'

baz = foo
baz() # returns 'bar'

ham = spam
ham() # returns 'eggs'
# storing in container

import math
functions = [math.sqrt, math.factorial, math.log]
functions[0](25) # returns 5.0
# the above equivalent to math.sqrt(25)
# passing as argument

class C:
    builtin_open = staticmethod(open)

# open function is passed
# to the staticmethod class