1

I have the following generator:

import random
import string

def random_string_generator(size = 10, chars = string.ascii_lowercase + string.digits):

    return "".join(random.choice(chars) for _ in range(size))

When I use interactive mode to inspect the generator, I get the following:

In [30]: random_string_generator()
Out[30]: '6v0vhljxac'

However, I do not understand how it works.

From what I've found so far:

  1. _ has no special meaning in the grammar of Python
  2. _ isn't in the chars list
  3. The generator works like (expression(x) for x in iterator)

I have tried to further decompose the code in interactive mode and this is what I've found:

In [38]: chars=string.ascii_lowercase + string.digits

In [39]: size=10

In [40]: (random.choice(chars) for _ in range(size))
Out[40]: <generator object <genexpr> at 0x10bc6b258>

In [41]: list( (random.choice(chars) for _ in range(size))
    ...: )
Out[41]: ['6', 'v', '3', 'd', 'm', 'c', 'h', '1', 'v', 'n']

So my question is how does the random.choice(chars) portion of the generator communicate with the iterative part for _ in range(size) when they have no apparent connection to each other?

Erick Shepherd
  • 1,403
  • 10
  • 19
AbstProcDo
  • 19,953
  • 19
  • 81
  • 138
  • 1
    https://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits-in-python/2257449#2257449 – Ignacio Vazquez-Abrams Nov 06 '17 at 17:27
  • 1
    Basically, you're doing `random.choice` `size` times. Since you don't care about which iteration you're on at any time, you don't need to keep track of that value in a variable. `_` is the conventional way of representing a value that you're "dumping" – Patrick Haugh Nov 06 '17 at 17:28
  • 1
    `_` is just like any other variable. Replace it with `x` and look at the generator expression again. It is only conventionally used as a way of saying "we don't actually use this value" when you need to assign to something. – juanpa.arrivillaga Nov 06 '17 at 17:49

2 Answers2

4

You're just calling random.choice(chars) size many times. The _ is just being used as a syntactic loop control variable but nothing is done with the int values it holds. It's comparable to a loop like the following:

for i in range(3):

    print("Spam, ham, and eggs.")

Which prints the following:

Spam, ham, and eggs.
Spam, ham, and eggs.
Spam, ham, and eggs.

In this example, the loop body never implements i but it still executes three times as the loop iterates through range(3).

Conventionally, underscores are often used as variable names when we don't care about the value held by a particular variable, such as a loop control variable or one or more values returned by a function with multiple return values. As an example of the latter:

def split_name(name):

    first_name, middle_name, last_name = name.split()

    return first_name, middle_name, last_name

_, _, surname = split_name("John R. Doe")

print(surname)

In this case, the split_name() function returns three values: A first name, middle name, and last name. However, the programmer might hypothetically only be interested in the third return value of the example function, so they might use _ to catch the first two returned values and use surname to collect the value they are actually interested in.

Erick Shepherd
  • 1,403
  • 10
  • 19
1

[a for a in range(10)] will produce [1, 2, 3, 4, 5, 6, 7, 8, 9] so that this syntax produce list.

[1 for _ in range(2)] will produce [1, 1] so that 1 is added 2 times in to row.

[random for _ in range(10)] will be produce 10 random variable.

str.join(list) is just a method of string that joins list elements into a string.