This is a toy project and I am a beginner in Python.
I need to create 10 random 3-letter strings, where characters are a
- z
.
This is my implementation:
import random
def random_char():
return random.randint(97, 122)
three_letter_words = []
for i in range(0, 10):
three_letter_words.append(chr(random_char()) + chr(random_char()) + chr(random_char()))
I really do not like the part where I call chr(random_char())
3 times.
How can I improve this to make it more compact and cleaner?
3 Answers 3
Python 3.6 introduced random.choices
, which allows you to write this even more succinctly than using random.choice
:
from random import choices
from string import ascii_lowercase
three_letter_words = ["".join(choices(ascii_lowercase, k=3)) for _ in range(10)]
-
1\$\begingroup\$ @KorayTugay It is shorter than
["".join(choice(ascii_lowercase) for _ in range(3)) for _ in range(10)]
. IMO this makes it also easier to read. Whether or not it is easier to read than writing out thefor
loop, that depends a bit on how used you are to list comprehensions. I think this one is not yet pushing it so far as to become unreadable. \$\endgroup\$Graipher– Graipher2018年07月10日 20:20:12 +00:00Commented Jul 10, 2018 at 20:20 -
2\$\begingroup\$ I would not recommend doing
from random import choices
.choices
isn't a comprehensive enough name to stand on its own like that.random.choices(...)
seems like a much more readable choice (pun intended). \$\endgroup\$Alexander– Alexander2018年07月10日 22:54:07 +00:00Commented Jul 10, 2018 at 22:54
a much more readable way of specifying that character range:
random.choice(string.ascii_lowercase)
To get a string of
n
characters:''.join([random.choice(string.ascii_lowercase) for _ in range(n)])
In addition to @l0b0's answer:
To generate cryptographically secure random strings (since this is a toy project, why not use a CSPRNG? it's a useful thing to learn):
import string
# only needs to be initialized once
import random
csprng = random.SystemRandom()
# alternatively, the "secrets" module is meant for generating strong csprng numbers
import secrets
csprng = secrets.SystemRandom()
# uses @Graipher's suggestion of .choices()
def random_string(len = 3, charsets = string.ascii_lowercase):
return ''.join(csprng.choices(charsets, k = len))
three_letter_strings = [random_string() for _ in range(10)]
print(three_letter_strings)
"""
example output:
['ebi', 'hbg', 'hlm', 'rhp', 'eka', 'uja', 'uhi', 'lju', 'vvf', 'qtj']
"""
"""
alternatively, below is likely faster
if you want to do this a lot more than 10 times
see: https://stackoverflow.com/a/2970789
"""
import itertools
three_letter_strings = [random_string() for _ in itertools.repeat(None, 10)]
print(three_letter_strings)
Tested with Python 3.6.5
-
1\$\begingroup\$ You should probably only be using one SystemRandom instance. Also, if you're
extend
ing an empty list with a generator expression you might as well just use a list comprehension. Lastly, your use ofitertools.repeat
just produces the same string 10 times - this may be random in the XKCD sense but probably isn't what OP was after. \$\endgroup\$Izaak van Dongen– Izaak van Dongen2018年07月10日 09:51:27 +00:00Commented Jul 10, 2018 at 9:51 -
\$\begingroup\$ @IzaakvanDongen thanks for your feedback, I've edited my answer to include your suggestions (and fixed
itrertools.repeat
, I'd forgotten that it returns the same thing N times, instead of calling it N times). \$\endgroup\$esote– esote2018年07月10日 11:35:12 +00:00Commented Jul 10, 2018 at 11:35 -
1\$\begingroup\$ Wow, it is actually faster:
%timeit [0 for _ in range(10000000)]
: 325 ms ± 1.38 ms,%timeit [0 for _ in repeat(None, 10000000)]
: 184 ms ± 1.72 ms. That's a factor 2! (Of course this is only relevant as long as looping dominates the timing and not the execution of the function itself.) \$\endgroup\$Graipher– Graipher2018年07月10日 11:59:18 +00:00Commented Jul 10, 2018 at 11:59