Change language

A performance comparison of def and lambda functions in Python 3.x

|

An idea for code

While reading pep8, I came across a paragraph about using anonymous functions - according to pep8, they reduce readability if you use a variable with a function value as a function, it's better to use def. I decided to compare def and lambda functions on another parameter - speed. Presumably, a lambda grounded for single lines will be faster to execute and create. In this study, I'll check it out.

Libraries

As there will be a lot of time measurements here, we will undoubtedly need the time library, and also turtle, to draw all sorts of graphs. I know it's impractical, but matprolib takes too long (10 seconds) to import. So:

from turtle import *
from time import time

Common functions

In our code, we need a function to measure performance. It will be the main one for all the derivatives. First of all, we will measure the execution time more than once - the error is too high. The function will take as arguments the function to be measured and the number of repetitions of that function.

For the measurement itself, we will use the time difference between the start of the execution and the end. The description makes up the code:

def speed_test(func, n):
    start = time()
    for i in range(n):
        func()
    stop = time()
    return stop - start

In total we will have 2 charts: a full chart and an average chart. Each has 2 charts: for def and lambda functions. In total we will need 4 turtles.

The list of values for graphs 1 and 2 is obvious - several results of speed measurements. With 3 and 4 it is more complicated - we need to find the arithmetic mean of one of the first 2 plots. So as not to get too bogged down in the graph, we will find the difference between each element of each graph and the average between the arithmetic averages of graphs 1 and 2. As a result, we will not see the total value on the graph, but the difference.

We will put all the graphs in a common dictionary, so as not to create many variables. The dictionary is declared in advance outside the function.

def graph_data(func1, func2, mult1, mult2, arr_len):
    l['l1'] = [func1(mult1)*mult2 for i in range(arr_len)]
    l['l2'] = [func2(mult1)*mult2 for i in range(arr_len)]
    l1_av = sum(l['l1']) // arr_len
    l2_av = sum(l['l2']) // arr_len
    av = sum((l1_av, l2_av)) / 2
    l['l3'] = [l1_av - av for i in range(arr_len)]
    l['l4'] = [l2_av - av for i in range(arr_len)]
    for i in range(arr_len):
        l['l1'][i] -= av
        l['l2'][i] -= av

Functions to make life easier

Who would want to repeat the same action, but with different parameters? No one. So, I've written some helper functions, to draw a graph according to given parameters, to create a turtle. Speaking of the latter, turtles are also entered into the common dictionary.

def draw(arr, t, x, mult=30):
    n = len(arr)
    t.up()
    t.goto(-n*mult/2, 0)
    for i, j in enumerate(arr):
        t.goto(x+(-n*mult/2+i*mult), j)
        t.down()
    t.up()
def add_turtle(name, color='#000000', width=2):
    t[name] = Turtle()
    t[name].pencolor(color)
    t[name].width(width)
    t[name].hideturtle()
    t[name].speed('fastest')

Derived functions

Weak-natured people who hate multi-level attachments should not read.

You can create infinitely many derivatives for the previously described general functions. For the speed measurement derivative, the structure is as follows:

def title(number of_repeats):
    def function_for_measurement():
        '''action'''
    return speed_test(function_for_measure,
                      number_of_repeats)

And the derivative for a graph function is the same function with defined arguments.

We are going to check the speed of creation and speed of execution of different kinds of functions.

Let's return to the first one. In the case of checking the speed of function creation, function_for_measure() will have one purpose - to create a def or lambda function inside itself. It will be called many times and each time it will create one and the same function anew. In other words - the second nesting level function serves to repeatedly call and create a third nesting level function during each.

I could have made it easier, but I wanted to keep the structure for all the derived functions.

The first two derivatives are for creating empty functions that return False. For def I could write using return or pass, but this is not possible in lambda.

def test_empty_def(n):
    def adding_def_func():
        def test(): return False
    return speed_test(adding_def_func, n)

def test_empty_lambda(n):
    def adding_lambda_func():
        test = lambda: False
    return speed_test(adding_lambda_func, n)

The next two are for the same functions, but with a simple expression:

def test_def(n):
    def adding_def_func():
        def test(): return sum((2, 3, 4)) ** 0.5
    return speed_test(adding_def_func, n)

def test_lambda(n):
    def adding_lambda_func():
        test = lambda: sum((2, 3, 4)) ** 0.5
    return speed_test(adding_lambda_func, n)

Two more to assess their speed of creation and speed of execution:

def test_def2(n):
    def adding_def_func():
        def test(): return sum((2, 3, 4)) ** 0.5
        test()
    return speed_test(adding_def_func, n)

def test_lambda2(n):
    def adding_lambda_func():
        test = lambda: sum((2, 3, 4)) ** 0.5
        test()
    return speed_test(adding_lambda_func, n)

These functions will be used in the graph_data derivatives:

def for_empty_func(arr_len):
    graph_data(test_empty_def, test_empty_lambda, 10000, 20000, arr_len)

def for_one_eval_func(arr_len):
    graph_data(test_def, test_lambda, 10000, 20000, arr_len)

def for_doing_func(arr_len):
    graph_data(test_def2, test_lambda2, 10000, 20000, arr_len)

Algorithm

Name the window:

title('Speed comparison of def and lambda functions')

Create four turtles to draw the graph:

t = {}
add_turtle('t1', '#c80000')
add_turtle('t2', '#00c800')
add_turtle('t3', '#c80000')
add_turtle('t4', '#00c800')

Define the length of the graph in vertices:

arr_len = 20

Prepare the data for the graphs and plot them:

l = {}
for i in range(5):
    производная_от_graph_data(arr_len)
    draw(l['l1'], t['t1'], -300)
    draw(l['l2'], t['t2'], -300)
    draw(l['l3'], t['t3'], 300)
    draw(l['l4'], t['t4'], 300)

Don't forget to add a close event to the window:

exitonclick()

Final algorithm:

title('Сравнение def и lambda функций по скорости')

t = {}
add_turtle('t1', '#c80000')
add_turtle('t2', '#00c800')
add_turtle('t3', '#c80000')
add_turtle('t4', '#00c800')

arr_len = 20
l = {}
for i in range(5):
    for_one_eval_func(arr_len)
    draw(l['l1'], t['t1'], -300)
    draw(l['l2'], t['t2'], -300)
    draw(l['l3'], t['t3'], 300)
    draw(l['l4'], t['t4'], 300)

exitonclick()

Tests

Let's move on to the main thing - which is faster? Green on the graph is lambda, red is def.

The first test is the speed of creation of an empty (almost) function:

Иллюстрация: def и lambda-функции

The second test is the speed of creating a function with an expression:

Иллюстрация: def и lambda-функции

The third test is the speed of creation and execution:

In all cases, lambda functions lead.

Conclusions

To improve readability in any case use def, but if speed is a priority - do not use Python. But seriously, this article may be useful for someone, because Python is ideal for some tasks, so why not optimize these tasks?

Shop

Learn programming in R: courses

$

Best Python online courses for 2022

$

Best laptop for Fortnite

$

Best laptop for Excel

$

Best laptop for Solidworks

$

Best laptop for Roblox

$

Best computer for crypto mining

$

Best laptop for Sims 4

$

Latest questions

NUMPYNUMPY

Common xlabel/ylabel for matplotlib subplots

12 answers

NUMPYNUMPY

How to specify multiple return types using type-hints

12 answers

NUMPYNUMPY

Why do I get "Pickle - EOFError: Ran out of input" reading an empty file?

12 answers

NUMPYNUMPY

Flake8: Ignore specific warning for entire file

12 answers

NUMPYNUMPY

glob exclude pattern

12 answers

NUMPYNUMPY

How to avoid HTTP error 429 (Too Many Requests) python

12 answers

NUMPYNUMPY

Python CSV error: line contains NULL byte

12 answers

NUMPYNUMPY

csv.Error: iterator should return strings, not bytes

12 answers

News


Wiki

Python | How to copy data from one Excel sheet to another

Common xlabel/ylabel for matplotlib subplots

Check if one list is a subset of another in Python

sin

How to specify multiple return types using type-hints

exp

Printing words vertically in Python

exp

Python Extract words from a given string

Cyclic redundancy check in Python

Finding mean, median, mode in Python without libraries

cos

Python add suffix / add prefix to strings in a list

Why do I get "Pickle - EOFError: Ran out of input" reading an empty file?

Python - Move item to the end of the list

Python - Print list vertically