How does the "for" inline command work?

Asked

Viewed 3,744 times

12

I made a question about a Python algorithm, but reply of user Anderson Carlos Woss he used a kind of for inline that I had never seen and that left me confused.

Follow the code section corresponding to for:

groups = [text[i:i+key].ljust(key, "*") for i in range(0, len(text), key)]

Could someone explain to me what this expression is for inline and how it works?

2 answers

18


This form is called list comprehension, or in English list comprehensions. It is nothing more than a simplified way of writing a loop for. In this case I even used it inappropriately, because the final code was not as readable as it should be in a Python code, but the code equivalent to this would be:

groups = []

for i in range(0, len(text), key):
    value = text[i:i+key]          # Pega o trecho da string de i até i+key
    value = value.ljust(key, "*")  # Garante que a string possua "key" caracteres, adicionando *
    groups.append(value)           # Adiciona o valor final à lista

This above code, for practical purposes, does exactly the same thing as the line presented in the question:

groups = [text[i:i+key].ljust(key, "*") for i in range(0, len(text), key)]

This syntax is defined to make the code more concise and much more readable - at least this is the goal (not always used correctly hehe). In unofficial sources, one can read that the understanding of lists is even faster than a for normal mainly due to internal language implementations. I’ve never particularly tried to prove this in practice and I don’t remember seeing any official source to prove it, but at the very least, it has the same performance, so you can use it fearlessly.

Even, one can use the understanding of nested form lists, for example, to traverse a "matrix", which requires a for for rows and one for columns.

matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

double_matrix = [[2*matrix[i][j] for j in range(3)] for i in range(3)]

The result would be the "matrix" multiplied by 2:

[
  [ 2,  4,  6],
  [ 8, 10, 12],
  [14, 16, 18]
]

For the equivalent code would be:

matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

double_matrix = []

for i in range(3):
    row = []
    for j in range(3):
        row.append(2*matrix[i][j])
    double_matrix.append(row)

References

Official documentation: List Comprehensions


This syntax is also valid for defining dictionaries. Suppose we want to create a dictionary with integer keys from 0 to 4, whose dictionary value refers to twice the key. With a loop for, we could do:

d = {}

for i in range(5):
    d[i] = 2*i

print(d)

See working on Ideone.

The syntax using Dict comprehensions would be:

d = {i: 2*i for i in range(5)}

See working on Ideone.

The main difference is that while understanding lists is defined between brackets, [], which is the syntax of lists, the understanding of dictionaries is defined between keys, {}, which is the syntax of dictionaries, besides defining the key/value pair and i: 2*i.


That said, it is possible to think that it would also be possible to define a tuple with this syntax only using parentheses, (), which is the tuple syntax. Wrong. The tuple is an immutable type of Python and therefore could not be changed next to the loop for, but that doesn’t mean that the syntax using parentheses is wrong.

In fact, when using parentheses, what is defined is a generator. That is, do something like:

g = (2*i for i in range(5))

See working on Ideone.

It would be the same as:

def temp():
    for i in range(5):
        yield 2*i

g = temp()

In these cases, you could iterate on the generator or simply convert it to a list:

l = list(g)

print(l)

See working on Ideone.

In some cases it would be possible to omit the parentheses, so when you see the syntax without the parentheses, know that it is also a definition of a generator. To quote an example, the calculation of the sum of the values between 0 and 5:

s = sum(i for i in range(5))

See working on Ideone.

  • It’s also possible to understand dictionaries, right? It works more or less like this I think

  • 1

    @Jeffersonquesado Yes, it is possible, as well as it is possible to define a generator with this syntax by removing the brackets.

11

This is basically the same as writing

for i in range(0, len(text), key)]
    groups = groups + [text[i:i + key].ljust(key, "*")

Do you know other languages? The range() generates a numerical sequence as if it were

for (int i = 0; i < len(text); i+= key)

The function ljust() makes a padding then it repeats a certain character as many times as necessary until it fills in the desired size. She does this with, an object previously obtained with the expression

text[i:i + key]

I put in the Github for future reference.

This is a Slice, i.e., it takes a collection of data, in this case a collection of characters, also known as string establishes in which character will start to pick, in case the value of i and until which position to take, in the case i + key, i.e., it takes the amount of characters from the increment of the for.

Documentation of for. And on flow control structures in general.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.