OOP Hangman Game — измененный код

Этот код изначально был размещен здесь. Упражнение по изучению ООП и 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

Это большое улучшение исходного кода — молодец! Несколько моментов:

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 слов ждут, пока вы их скопируете и вставите.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *