Hint: You can adjust the default video playback speed in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Timing Functions With Decorators

In this lesson, you’ll see how to use decorators to measure the time a function takes to execute and print the duration to the console.

Comments & Discussion

Alan Eng on May 7, 2019

Mind = blown. The real-world examples really solidify my knowledge! Thanks.

LJIN Lab on Aug. 10, 2019

I’ve been timing my functions like a freaking caveman. I love you man.

David on May 7, 2020

I can’t spot my mistake! :( On Section 3.2 Both Moduloes in the same directory…

def timer(func):
    """Print the runtime of the decorated function"""
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()  # 1
        value = func(*args, **kwargs)
        end_time = time.perf_counter()  # 2
        run_time = end_time - start_time  # 3
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value
    return wrapper_timer()
@timer
def waste_some_time(num_times):
    for _ in range(num_times):
        sum([i**2 for i in range(10000)])

Ctrl+S Open a fresh REPL

$ bpython
bpython version 0.19 on top of Python 3.7.5 /usr/bin/python3
>>> from examples import waste_some_time
Traceback (most recent call last):
  File "<input>", line 1, in <module>
    from examples import waste_some_time
  File "/home/david/core/examples.py", line 9, in <module>
    @timer
  File "/home/david/core/decorators.py", line 33, in timer
    return wrapper_timer()
  File "/home/david/core/decorators.py", line 28, in wrapper_timer
    value = func(*args, **kwargs)
TypeError: waste_some_time() missing 1 required positional argument: 'num_times'
>>> 

Other examples so far worked perfectly fine… Thanks in advance :)

David on May 7, 2020

Found the mistake (A good hint that I need to sleep LOL): When defining the decorator, instead of return the function object, I returned the function calling. instead of:

def timer(func):
    """Print the runtime of the decorated function"""
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()  # 1
        value = func(*args, **kwargs)
        end_time = time.perf_counter()  # 2
        run_time = end_time - start_time  # 3
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value
    return wrapper_timer()

Do this:(last line)

def timer(func):
    """Print the runtime of the decorated function"""
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()  # 1
        value = func(*args, **kwargs)
        end_time = time.perf_counter()  # 2
        run_time = end_time - start_time  # 3
        print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value
    return wrapper_timer #  no "()" here, we need the object to 
                         #  be returned.

Pierre on June 20, 2020

Hi Christopher,

Thanks for the great tutorial. I never could wrap my head around the concept of decorators until this video series. This and the other course I just watched on generators were, alone, so well worth the cost of the subscription to Real Python. I particularly appreciate the pace. You seem to have found the sweet spot between too slow and too fast (at least for my level of Python literacy).

One question, I’ve never seen the phrasing for _ in range(n). Is the _ just a placeholder that can be used where you don’t need to reference what seems to amount to the index of the range?

Ricky White RP Team on June 20, 2020

That’s correct, Pierre. It’s often used in Python for variables that don’t need to be referenced again. If you find yourself using it inside of the for loop later on, you should refactor and give the variable a more semantic name.

Become a Member to join the conversation.