Sorting lists with multiple parameters using lambda expression

3

Given the Ponto class, the ordenar function sorts the list elements by following the following criteria: first value in x , then y and finally value in z .

Beauty, the code works. But I would like to understand how this lambda expression is "executed in practice" (what it does underneath the cloths, let's say). How can something relatively complex be run in a single line of code?

Complete code:

import random


class Ponto():
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return str(self.x) + ' ' + str(self.y) + ' '+str(self.z) 


def cria_pontos(quantos=10):
    lista = []
    for i in range(quantos):
        x = random.randint(0, 100) 
        y = random.randint(0, 100) 
        z = random.randint(0, 100)
        p = Ponto(x=x, y=y, z=z)
        lista.append(p) 
    return lista


def ordernar(lista=None):
    lista.sort(key=lambda p: (p.x, p.y, p.z))


def main():
    pontos = cria_pontos(quantos=5)
    pontos.append(Ponto(x=1,y=2,z=3))
    pontos.append(Ponto(x=2,y=3,z=2))
    pontos.append(Ponto(x=2,y=2,z=2))

    ordernar(lista=pontos)
    for p in pontos:
        print(p)

if __name__ == '__main__':
    main()

I created this class Ponto to use as an example to make it easier for me to understand my question. Please do not mind the design of it.

    
asked by anonymous 01.11.2017 / 00:18

1 answer

5

The sort method of the list class, when used with the key parameter will execute the ordering according to the value returned by the expression defined in key , not the value present in the list itself. For your case, use:

key=lambda p: (p.x, py. p.z)

That is, the expression to be used will be a lambda expression that will convert an object of type Ponto into a tuple with its point coordinates. Natively, in Python, when comparing two tuples, the comparison is given value by value, from left to right, that is, compares x, then y, and finally z.

So, considering a brief list of points:

lista = [Ponto(x=1,y=2,z=3), Ponto(x=2,y=3,z=2), Ponto(x=2,y=2,z=2)]

When you run the sort function:

lista.sort(key=lambda p: (p.x, p.y, p.z))

Roughly speaking, what happens is:

  • Defines an auxiliary list with the first value in the list: saida = [Ponto(x=1,y=2,z=3)] ;
  • It takes the second value from the list, iterates over the auxiliary list and applies the lambda expression both in the value to be added and in the present value in the auxiliary list, comparing its returns:
    • The return of the lambda expression for the value to be added will be: (2, 3, 2) ;
    • Scrolls the auxiliary list and the return of the expression to the value in the auxiliary list will be: (1, 2, 3) ;
    • Compares the two tuples returned and, as the tuple of the value to be added will be after the tuple returned to the value in the auxiliary list, the value is inserted after;
    • The auxiliary list is then: saida = [Ponto(x=1,y=2,z=3), Ponto(x=2,y=3,z=2)] ;
  • Get the third value from the list and repeat the process:
    • The return of the lambda expression of the first value of the auxiliary list will be: (1, 2, 3) ;
    • The return of the lambda expression to the value added will be: (2, 2, 2) ;
    • Comparing them, it turns out that the new value should be added after the first;
    • Then the same comparison is made to the second value:
    • The return of the lambda expression of the second value of the auxiliary list will be: (2, 3, 2) ;
    • Comparing them, it turns out that the new value must be entered before the second;
    • Thus, the new value is inserted in the auxiliary list between the first and second values, being: saida = [Ponto(x=1,y=2,z=3), Ponto(x=2,y=2,z=2), Ponto(x=2,y=3,z=2)] ;
  • After the iterations are finished, the new list value is defined as the auxiliary list: lista = saida ;
  • In this way, after sorting, the list would look like:

    lista = [Ponto(x=1,y=2,z=3), Ponto(x=2,y=2,z=2), Ponto(x=2,y=3,z=2)]
    

    Sorted according to the values of x, y and z, respectively. The performance of the function is not greatly affected by the use of the key parameter because, due to internal implementations of the language, the expression defined in key is called only once for each value in the list, regardless of how many times that value is used in the ordering process.

        
    01.11.2017 / 00:38