Этот код изначально был размещен здесь. Упражнение по изучению ООП и Python. Огромное спасибо за время и усилия, которые @ferada @Alex Waygood @ m-alorda и другие вложили в его комментарии.
Вы можете запустить его как ответ здесь:
https://replit.com/@scripta/Hangman#main.py
По большинству поднятых вопросов приняты меры. Еще предстоит решить несколько вопросов.
Качество разбиения на объекты:
Должен ли существовать объект «Игра» или логику вождения следует оставить в виде отдельных процедур? Кажется, есть необходимость сгруппировать логику управления (и вспомогательные функции) либо как отдельный модуль, либо как объект. Не все ли равно что?
А как насчет класса Виселица? В каком-то смысле виселица — это свидетельство неверных догадок. Однако у него есть то преимущество, что он знаком с точки зрения игры. Должны ли догадки быть свойством объекта Game?
Как определить, что делает объект хорошим? Простота? Знакомство с предметом? …? Какие-нибудь мысли?
Именование методов:
Object.method (), казалось, оставлял место для лаконичного и естественного английского подхода при вызове таких методов, как if target.guessed () … Это может сбивать с толку имена методов при изолированном чтении (в определении класса). Кто-нибудь еще хочет внести свой вклад в это?
Надеюсь, ничего не было упущено. Теперь, когда код был очищен, есть еще какие-нибудь комментарии?
play_hangman.py (использует объекты Word, Guesses и Gallows в hangman.py)
import hangman
def get_letter(guesses):
while True:
char= input("nPlease enter your guess: ")
if len(char) < 1:
print ("You didn't choose a letter!")
elif len(char)!= 1:
print ("One letter at a time!")
elif not char.isalpha():
print ("Alphabetic characters only!")
elif guesses.guessed (char):
print ("You already guessed that letter")
else:
break
return (char)
def display_progress(target, gallows, guesses):
print("n",target.progress(guesses))
print(gallows.draw())
print("nUsed: n",guesses.made())
def play_game():
target = hangman.Word()
guesses = hangman.Guesses()
gallows = hangman.Gallows()
while True:
guess = get_letter(guesses)
guesses.record(guess,target,gallows)
display_progress(target, gallows, guesses)
if target.guessed(guesses):
print("nYou win, well done")
break
if gallows.hanged():
print ("nI win. The word was:",target.word)
break
if __name__ == "__main__":
play_game()
Hangman.py
import random
class Word:
WORDS = ("foxglove", "captain", "oxygen", "microwave", "rhubarb")
def __init__(self):
self._word = random.choice(self.WORDS)
self.letters_in_word = set(self._word)
@property
def word(self):
return self._word
def progress(self, guesses):
# create string of underscores and guessed letters to show progress to guessing word
progress_string = ""
for char in self.word:
if guesses.guessed(char):
progress_string += f" {char} "
else:
progress_string += " _ "
return(progress_string)
def guessed(self, guesses):
letters_guessed= self.letters_in_word.intersection(guesses.guesses_made)
return letters_guessed == self.letters_in_word
class Guesses:
def __init__(self):
# guesses holds all guesses made (wrong or right)
self.guesses_made = set()
def guessed(self,char):
return char in self.guesses_made
def record(self,guess,word,gallows):
# All valid guesses (wrong or right) are added to the guesses set
self.guesses_made.add(guess)
if guess not in word.letters_in_word:
gallows.record_bad_guess()
def made(self):
guesses_list = sorted(self.guesses_made)
#comma separate the guesses
guesses_string = ",".join(guesses_list)
return guesses_string
class Gallows:
bad_guesses = 0
GALLOWS_IMAGES= (" n n n n n__________",
" n | n | n | n | n_|________",
" _____n | n | n | n | n_|________",
" _____n |/ n | n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n | | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/" n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | / n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | / \n_|________",)
def record_bad_guess(self):
self.bad_guesses += 1
def hanged(self):
return self.bad_guesses >= len(self.GALLOWS_IMAGES) -1
def draw(self):
return self.GALLOWS_IMAGES[self.bad_guesses]
1 ответ
Это большое улучшение исходного кода — молодец! Несколько моментов:
1. Все еще нет PEP8-соответствует 🙁
В вашем коде все еще есть много мест, где у вас нет пробелов вокруг операторов (извините, это мой багряный медведь!), И несколько других нарушений PEP8. elif len(char)!= 1
должно быть elif len(char) != 1
; guesses.guessed (char)
должно быть guesses.guessed(char)
; print("n",target.progress(guesses))
должно быть print("n", target.progress(guesses))
и т. д. Если вы используете IDE, например PyCharm, это очень поможет вам в этом, так как автоматически помечает части вашего кода, которые не соответствуют PEP8.
Еще одна вещь, которую вы могли бы добавить в свой код, чтобы сделать ваш код более читабельным для других людей, — это строки документации, которые очень ценны.
2. Является ли Game
класс нужен?
Ваш код кажется мне гораздо более логичным в том виде, в котором он есть сейчас, без Game
учебный класс. Я бы, наверное, не назвал главный файл play_hangman.py
, однако — вам, вероятно, следует назвать каталог, в котором находятся оба файла hangman
, и вызовите файл, который в настоящее время называется play_hangman.py
main.py
. Файлы под названием main.py
обычно понимаются пользователями Python как основная точка входа в проект Python.
Что касается вашего более широкого вопроса о том, как вы должны концептуализировать объекты в объектно-ориентированном программировании … это зависит от обстоятельств. Одна из замечательных особенностей Python — это то, насколько легко он позволяет комбинировать элементы из нескольких парадигм программирования. В некоторых сценариях я пишу исключительно процедурный код, в других — исключительно объектно-ориентированный код, а в других — чрезвычайно функциональный код. В этом случае определенная доля объектно-ориентированного стиля имеет смысл для проекта, но не стоит перебарщивать. Программировать питонически — значит ставить элегантность и простоту выше любых «правил», связанных с той или иной парадигмой программирования. Ты не нужно а Game
class здесь, и это на самом деле не делает ваш код более эффективным, элегантным или легким для понимания … так зачем же он?
3. Является ли Gallows
класс нужен?
Я думаю, ты прав, что твоя Gallows
class как таковой в настоящее время не выполняет большой работы. Однако, как вы говорите, легко понять, что такое Gallows
класс мог бы сделать. Во всяком случае, идея Guesses
немного сложнее осмыслить класс. Думаю, я мог бы объединить твои Guesses
и Gallows
такие классы:
class Gallows:
"""The `Gallows` class records the guesses you've made,
and delivers upon you a slow and painful death
should you make too many incorrect guesses.
"""
GALLOWS_IMAGES = (
" n n n n n__________",
" n | n | n | n | n_|________",
" _____n | n | n | n | n_|________",
" _____n |/ n | n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n | | n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/" n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | / n_|________",
" _____n "https://codereview.stackexchange.com/"n | O n "https://codereview.stackexchange.com/"\ n | / \n_|________"
)
GALLOWS_IMAGES_LEN = len(GALLOWS_IMAGES)
def __init__(self):
# `guesses_made` holds all guesses made (wrong or right)
self.guesses_made = set()
self.incorrect_guesses = 0
def character_already_guessed(self, char):
return char in self.guesses_made
def record_guess(self, guess, word):
# All valid guesses (wrong or right) are added to the `guesses_made` set
self.guesses_made.add(guess)
if guess not in word.letters_in_word:
self.incorrect_guesses += 1
def all_guesses_made(self):
guesses_list = sorted(self.guesses_made)
# comma-separate the guesses
guesses_string = ",".join(guesses_list)
return guesses_string
def hanged(self):
return self.bad_guesses >= self.GALLOWS_IMAGES_LEN - 1
def draw(self):
return self.GALLOWS_IMAGES[self.bad_guesses]
Если вы пойдете по этому пути, вам нужно будет убедиться, что вы обновили файл, который в настоящее время называется play_hangman.py
так что он не содержит ссылок на Guesses
класс, которого больше не существует.
4. Места, в которых вы могли бы подумать о следующем проекте.
Я бы сказал, что главная нерешенная проблема вашего проекта заключается в том, что вы пытаетесь угадать всего несколько слов. Одним из решений этого может быть текстовый файл с несколькими сотнями или даже несколькими тысячами слов, который Python мог бы довольно легко и быстро загрузить в ваш Word
класс по инициализации вашей программы. Если слова сохранены в words.txt
file и разделены символами новой строки, вы можете сделать это так:
class Word:
WORDS_FILE = 'words.txt'
with open(WORDS_FILE, 'r') as f:
WORDS = tuple(f.read().splitlines())
# <--- snip --->
Вот список 58 000 слов ждут, пока вы их скопируете и вставите.