How can I refactor this to use classes?
You don't. There is absolutely no need for that in this program as you don't share a common state between multiple functions. On the same subject, see also this PyCon talk.
Instead, we should focus on clearing the obvious mistakes made in this function:
- Be sure to pass information to functions using parameters: global variable can be fine for constants, not so much for information to work on; besides if I
import
your code in an interactive session, I am greeted with a game but any other attempt to call guess_the_word()
will reuse the same word to guess.
- This also lead to how you call your code: running the program on the command line will play a game, but
import
ing it will play it too; which may be undesirable. To avoid this side effect, get yourself familiar with the if __name__ == '__main__':
construct.
secrets
is a module designed for cryptographically secure sources of random numbers. You don't need such strong randomness here so you can avoid the associated overheads using the simpler random
module.
- You can
join
directly on lists: no need to write a list-comprehension which will just copy the underlying list before feeding it to join
. In the same vein, strip
ing whitespace and converting to str
is superfluous here as the result of join
already fulfill the two conditions.
- Checking if a letter is in a string is an \$\mathcal{O}(n)\$ operation, so you can skip that and directly perform the iteration over each letter it won't cost more. You can use a flag to know if you have found a matching letter.
- A message at the end of the while loop to conclude would be a great addition to the user experience.
All in all, this could yield the following code:
import random
def guess_the_word(secret_word):
tries_left = 9
output = ["_"] * len(secret_word)
while tries_left >= 0 and secret_word != ''.join(output):
print(' '.join(output))
print("\nYou have", tries_left, "tries left")
guessed_letter = input("Guess a letter from secret word: ")
found = False
for i, letter in enumerate(secret_word):
if guessed_letter == letter:
output[i] = guessed_letter
found = True
if found:
print("You guessed correct")
else:
tries_left -= 1
print("You guessed incorrect")
if tries_left < 0:
print("\nNo more tries, you lose")
else:
print("\nCongratz! You found the secret word:", secret_word)
def main(filename="words.txt"):
print("\nWelcome to Hangman Game!\n")
with open(filename) as words_file:
words = words_file.read().split()
guess_the_word(random.choice(words))
if __name__ == '__main__':
main()
An other nice addition to have is the ability to repeat the games automatically. You could change your main
part to:
def main(filename="words.txt", repeat=sys.maxsize):
print("\nWelcome to Hangman Game!\n")
with open(filename) as words_file:
words = words_file.read().split()
for _ in range(repeat):
guess_the_word(random.choice(words))
if __name__ == '__main__':
main(repeat=1)
to keep the current behaviour but allows import
ing the file to run main()
the desired amount of time without having to reload the file each time.