diff --git a/OOP_task.md b/OOP_task.md new file mode 100644 index 000000000..c341efc5f --- /dev/null +++ b/OOP_task.md @@ -0,0 +1,221 @@ +# Модульное задание по OOP + +Создаем консольную игру камень-ножницы-бумага + +## Общее описание + +Нам нужно создать консольную игру в которой игрок будет выбирать свой ход из 3 вариантов (камень, ножницы, бумага) путем +ввода в консоль одной из 3 цифр 1, 2 или 3. +Вымышленный соперник, будет выбирать свой вариант случайно из таких же трех вариантов. + +Игра идет до тех пор, пока у игрока есть `жизни`, если атака успешна, или соперник умер, должны быть начислены очки в +зависимости от выбранного уровня сложности. Если игрок умер, очки должны быть записаны в файл с результатами. + +## Необходимые файлы и папки + +- `main.py` - основной скрипт, отвечает за меню и за запуск игры +- Папка `game` - со всеми основными файлами игры + Все остальные файлы должны быть в этой папке +- `models.py` - файл с описанием моделей которые есть в игре (игрок и соперник) +- `settings.py` - файл со всеми константами которые могут нам понадобиться +- `exceptions.py` - файл с необходимыми нам исключениями +- `game.py` - файл с основной логикой игры +- `score.py` - файл с логикой сохранения очков игры + +## Разберем каждый файл + +### models.py + +Должен содержать два класса + +- `Player` +- `Enemy` + +#### Player + +Аттрибуты: + +- `name` - Имя игрока, задается пользователем через консоль +- `lives` - Количество жизней, берется из константы из `settings.py` +- `score` - Очки игрока, изначально 0 + +Методы: + +- `__init__` - для инициализации игрока, принимает только имя, назначает имя, кол-во жизней и очков. +- `select_attack` - метод для ввода атаки игроком. Вводим до тех пор, пока пользователь не введет валидное значение (1, 2, + 3), использует константы из файла `settings.py` +- `decrease_lives` - метод, который будет вызываться если игрок проиграл "бой", уменьшает жизни на 1. Если жизни + закончились, вызывает исключение `GameOver` из файла `exceptions.py` +- `add_score` - метод для начисления очков игроку. + +#### Enemy + +Аттрибуты: + +- `lives` - Кол-во жизней, изначально зависит от уровня соперника и уровня сложности, уменьшается на 1 когда соперник + проигрывает "бой" +- `level` - уровень соперника, будет увеличиваться с каждым новым соперником. Изначально 1. + +Методы: + +- `__init__` - для инициализации соперника, принимает только уровень и сложность, что бы вычислить кол-во жизней, + назначает кол-во жизней и уровень. +- `select_attack` - метод для случайного выбора атаки (1, 2, 3), использует константы из файла `settings.py` +- `decrease_lives` - уменьшает жизни при проигрыше "боя", вызывает исключение `EnemyDown` из файла `exceptions.py` если у + соперника закончились жизни + +### game.py + +Содержит класс игры `Game` + +### Game + +Аттрибуты: + +- `player` - объект игрока +- `enemy` - объект соперника, при убийстве будет создан новый, с более высоким уровнем +- `mode` - уровень сложности, normal или hard, содержит либо 1, либо 2, которые определены константами + +Методы: + +- `__init__` - принимает объект игрока и уровень сложности, создает первого соперника +- `create_enemy` - метод для создания нового соперника +- `play` - метод запуска игры. Запускает бесконечный цикл в одной итерации которого происходит "бой". Для этого вызывает + два метода, `fight` и `handle_fight_result`. Отслеживает не произошло ли одно из исключений при вызове второго + метода `GameOver` или `EnemyDown`, при первом завершает игру и вызывает метод для записи очков, при втором создает + нового, более сильного соперника. +- `fight` - метод запрашивает у пользователя и соперника атаки, из констант получает результат боя (-1, 0, 1) +- `handle_fight_result` - принимает результат боя, и в зависимости от результата отнимает жизни либо у игрока, либо у + соперника. +- `save_score` - вызывает сохранение очков при помощи вызова класса из файла `score.py` + +### exceptions.py + +Классы + +- `GameOver` - когда у игрока заканчиваются жизни +- `EnemyDown` - когда у соперника заканчиваются жизни + +### settings.py + +Содержит все константы + +Примерное содержание: + +```python +MODE_NORMAL = 'Normal' +MODE_HARD = 'Hard' +MODES = {'1': MODE_NORMAL, + '2': MODE_HARD} + +PLAYER_LIVES = 2 +POINTS_FOR_FIGHT = 1 +POINTS_FOR_KILLING = 5 +MAX_RECORDS_NUMBER = 5 +HARD_MODE_MULTIPLIER = 2 + +SCORE_FILE = 'scores.txt' + +PAPER = 'Paper' +STONE = 'Stone' +SCISSORS = 'Scissors' + +WIN = 1 +DRAW = 0 +LOSE = -1 + +ALLOWED_ATTACKS = { + '1': PAPER, + '2': STONE, + '3': SCISSORS +} + +ATTACK_PAIRS_OUTCOME = { + (PAPER, PAPER): DRAW, + (PAPER, STONE): WIN, + (PAPER, SCISSORS): LOSE, + (STONE, PAPER): LOSE, + (STONE, STONE): DRAW, + (STONE, SCISSORS): WIN, + (SCISSORS, PAPER): WIN, + (SCISSORS, STONE): LOSE, + (SCISSORS, SCISSORS): DRAW +} +``` + +### score.py + +Классы: + +- `ScoreHandler` - класс для обработки очков +- `GameRecord` - класс содержащий записи об игроках +- `PlayerRecord` - класс для хранения записи об одном игроке + +#### ScoreHandler + +Аттрибуты: + +- `game_record` - объект класса GameRecord, туда мы будем считывать сохраненные очки и записывать таблицу с новыми +- `file_name` - имя файла откуда и куда мы записываем очки + +Методы: + +- `__init__` - принимает только имя файла и сохраняет его. Вызывает метод для чтения файла. +- `read` - метод, который будет читать файл и каждую его строку сохранять в `PlayerRecord`, которые будут сохранятся в + `GameRecord`. +- `save` - метод, который нужен, что бы записать новые результаты в файл (предварительно отсортировать и обрезать, если + нужно) +- `display` - метод для отображения очков + +#### GameRecord + +Аттрибуты: + +- `records` - список объектов типа PlayerRecord + +Методы: + +- `__init__` - создает объект с пустым списком записей +- `add_record` - метод для добавления записи об одном игроке. Должен проверять нет ли у нас уже такого игрока, и если + есть, то перезаписывать его результат. Тот же самый игрок проверяется по имени и уровню сложности (игрок может быть + представлен в таблице два раза на разном уровне сложности). (Можно использовать меджик метод `__eq__` для поиска + через `in`) +- `prepare_records` - метод для сортировки существующих результатов и обрезки до максимального кол-ва указанного в + настройках + +#### PlayerRecord + +Аттрибуты: + +- `name` - имя игрока +- `mode` - уровень сложности +- `score` - кол-во очков + +Методы: + +- `__init__` - для создания объекта принимает все три параметра +- `__gt__` - для того что бы можно было отсортировать записи по очкам +- `__str__` - для удобного вывода данных + +### main.py + +Содержит функции: + +- `main` - для запуска всего кода, внутри этой функции должен быть запущен процесс выбора из трех пунктов (Запуск игры, + посмотреть очки и выйти из игры (1,2,3)) +- `play_game` - вызывается если игрок выбрал начать игру, в этой функции будет запущен процесс создания игрока, создание + объекта игры и запуск самой игры +- `create_player` - спросить игрока имя и сложность, создать объект игрока с указанным именем, и передать объект игрока и + сложность в класс игры. +- `show_scores` - показать очки, используя класс `ScoreHandler` +- `exit` - выйти из игры + +## Пример как выглядит содержимое файла с очками + +| Name | Mode | Score | +|------|--------|-------| +| Vlad | Normal | 14 | +| Test | Hard | 12 | +| Jack | Normal | 12 | +| Vlad | Hard | 10 | +| Jack | Hard | 9 | \ No newline at end of file diff --git a/PR_explanation.md b/PR_explanation_Django.md similarity index 99% rename from PR_explanation.md rename to PR_explanation_Django.md index ad7d8a20a..76e38cb0c 100644 --- a/PR_explanation.md +++ b/PR_explanation_Django.md @@ -1,4 +1,4 @@ -# Как сдавать домашки +# Как сдавать домашки по Django При помощи пулл-реквеста на GitHub. diff --git a/PR_explanation_OOP.md b/PR_explanation_OOP.md new file mode 100644 index 000000000..11a7a4d4f --- /dev/null +++ b/PR_explanation_OOP.md @@ -0,0 +1,64 @@ +# Как сдавать домашки + +При помощи пулл-реквеста на GitHub. + +## Что должно быть в репозитории?? + +- Код проекта + +## Чего не должно быть в репозитории?? + +Если коротко, то ничего лишнего. + +- Папок `__pycache__` и любого его содержимого. +- Файлов настроек `IDE` например папки `.idea` или аналогов для других `IDE` +- Для маков, файлов `.DS_Store` + +Все эти файлы и папки должны быть занесены в `.gitignore` + +## Как создать проект и репозиторий корректно (Делается один раз когда нам необходимо создать новый проект) + +Действия по пунктам: + +1. Создать репозиторий (`git init`) +2. Подключить репозиторий к проекту (`git remote ...`) +3. Создать и заполнить `.gitignore` +4. Убедиться, что мы работаем в ветке `master` или `main` +5. Закоммитить всё что у вас есть в свежем проекте. (Только файл `.gitignore`) + + Для коммита делаем две команды + + `git add .` + + `git commit -m "Some text"` +6. Запушить пустой проект (только .gitignore) в мастер или мейн. (`git push`) +7. Добавить меня в колабораторы проекта. Ник на гитхабе (`PonomaryovVladyslav`) + +## Как делать домашку + +1. Создать **из мастера** ветку, в названии которой будет информация о том, что это домашка и её номер. Например `hw_3` +2. Выполнить домашнюю работу :) Рекомендуется коммитить изменения после любого осмысленного блока. По одной задаче или + даже куску задачи. +3. Выполнить **пуш** этой ветки на удалённый репозиторий. (`git push`) +4. Создать **пулл-реквест** из ветки с вашей **домашней работы** на **мастер** (онлайн на сайте github) +5. Добавить меня в раздел `Reviewers` +6. Дождаться моего апрува или комментариев, что нужно изменить + + 6.1. Если получили коментарии о необходимости изменений, изменения добавляем (`git add` + `git commit`) на ветку + **домашней работы** после чего выполняем **пуш** (`git push`), пулл реквест сам обновится, + и я получу уведомление об этом автоматически. +7. После того как получили апрув, мержим изменения в мастер. (онлайн на сайте) + +## Что делать если я уже внёс файлы \__pycache__ или .idea на гит? + +В этом случае вам надо удалить файлы из гита, но сохранить их на локальной версии. + +Для этого необходимо выполнить команду: + +``` +git rm --cached +``` + +Для каждого файла или папки. + +После чего выполнить коммит (`git commit`) и пуш (`git push`) снова. \ No newline at end of file diff --git a/lesson16.md b/lesson16.md index 3b7dbb187..0f66f1e36 100644 --- a/lesson16.md +++ b/lesson16.md @@ -170,7 +170,10 @@ d.hi() ```python >> D.mro() -[ , < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object'>] +[ < + + +class '__main__.D'>, < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object' >] ``` Обратите внимание, в конце всегда будет `object`, если вы используете любой Python версии 3. @@ -179,7 +182,7 @@ d.hi() Если по какой-то причине вас не устраивает существующий порядок, есть возможность вызвать метод ровно из того класса, откуда вам надо, но это считается плохой практикой и лучше так не делать, а полностью поменять структуру. -Если вам необходимо использовать метод конкретного родителя, например, `hi()` класса С, нужно напрямую вызвать его по +Если вам необходимо использовать метод конкретного родителя, например, `hi()` класса С, нужно напрямую вызвать его по имени класса, передав `self` в качестве аргумента: ```python @@ -196,12 +199,12 @@ d.call_hi() ## Magic methods (Они же иногда называются dunder-методы) -Что такое магические методы? Они всё в объектно-ориентированном Python. -Это специальные методы, с помощью которых вы можете добавить в ваши классы «магию». +Что такое магические методы? Они всё в объектно-ориентированном Python. +Это специальные методы, с помощью которых вы можете добавить в ваши классы «магию». Они всегда обрамлены двумя нижними подчеркиваниями, два вначале, два в конце (например, `__init__` или `__lt__`). -Это методы, которые отвечают за любые действия под "капотом". Те, которые выполняются неявно. Допустим, -вам нужно сложить два объекта через оператор `+`, нам нужен магический метод `__add__`, или мы хотим поменять поведение +Это методы, которые отвечают за любые действия под "капотом". Те, которые выполняются неявно. Допустим, +вам нужно сложить два объекта через оператор `+`, нам нужен магический метод `__add__`, или мы хотим поменять поведение при удалении `__del__`, поведение при переборе объекта в цикле `__iter__` или `__next__` и т. д. Почти любое действие, которое вам кажется выполняется само собой, скорее всего описано в magic методе. @@ -224,11 +227,12 @@ a.some_arg_one a.some_arg_two # 22 ``` -Такой способ предпочтительнее, чем вначале задавать какое-то бесполезное значение и сразу же его поменять (номер + +Такой способ предпочтительнее, чем вначале задавать какое-то бесполезное значение и сразу же его поменять (номер телефона или место на доске из прошлой домашки). -Когда я пишу `x = SomeClass()`, `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт -метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится +Когда я пишу `x = SomeClass()`, `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт +метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится метод `__del__`. Давайте подробнее рассмотрим эти три магических метода: @@ -236,33 +240,33 @@ a.some_arg_two `__new__(cls, [...)` Это первый метод, который будет вызван при инициализации объекта. Он принимает в качестве параметров класс и потом любые -другие аргументы, которые будут переданы в `__init__`. Метод `__new__` используется весьма редко, но иногда бывает -полезен, в частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. +другие аргументы, которые будут переданы в `__init__`. Метод `__new__` используется весьма редко, но иногда бывает +полезен, в частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. Я не намерен очень детально останавливаться на `__new__`, так как он не очень часто нужен, и этот метод очень хорошо и детально описан в документации. `__init__(self, [...)` Инициализатор класса. Ему передаётся всё, с чем был вызван первоначальный конструктор (так, например, если мы вызываем -`x = SomeClass(10, 'foo')`, `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно +`x = SomeClass(10, 'foo')`, `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно используется при определении классов. `__del__(self)` -Если `__new__` и `__init__` образуют конструктор объекта, то `__del__` - это его деструктор. Он не определяет поведение -для выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то +Если `__new__` и `__init__` образуют конструктор объекта, то `__del__` - это его деструктор. Он не определяет поведение +для выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то время, когда объект попадает в сборщика мусора. Это может быть довольно удобно для объектов, которые могут требовать дополнительных чисток во время удаления, таких как сокеты или файловые объекты. Однако, нужно быть осторожным, так как нет гарантии, что `__del__` будет вызван, если объект продолжает жить, когда интерпретатор завершает работу. Поэтому `__del__` не может служить заменой для хороших программистских практик (всегда завершать соединение, если -закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова `__del__` не должен +закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова `__del__` не должен использоваться почти никогда; используйте его с осторожностью. #### Магические методы сравнения В Python огромное количество магических методов, созданных для определения интуитивного сравнения между объектами при -помощи операторов, а не плохо выглядящих методов (Что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют -способ переопределить поведение Python по умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что +помощи операторов, а не плохо выглядящих методов (Что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют +способ переопределить поведение Python по умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что они делают: `__eq__(self, other)` @@ -442,9 +446,10 @@ class Word(str): `__unicode__(self)` -Определяет поведение функции unicode(), вызванной для экземпляра вашего класса. `unicode()` похож на `str()`, но возвращает -строку в Unicode. Будьте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только -`__unicode__()`, то это не будет работать. Постарайтесь всегда определять `__str__()` для случая, когда кто-то не имеет +Определяет поведение функции unicode(), вызванной для экземпляра вашего класса. `unicode()` похож на `str()`, но +возвращает +строку в Unicode. Будьте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только +`__unicode__()`, то это не будет работать. Постарайтесь всегда определять `__str__()` для случая, когда кто-то не имеет такой роскоши как Unicode. `__format__(self, formatstr)` @@ -543,7 +548,7 @@ class Word(str): означает то же, что и `x.__call__()`. Заметьте, `__call__()` принимает произвольное число аргументов; то есть вы можете определить `__call__()` так же как любую другую функцию, принимающую столько аргументов, сколько вам нужно. -`__call__()`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» +`__call__()`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» экземпляр может быть интуитивно понятным и элегантным способом изменить состояние объекта. Примером может быть класс, представляющий положение некоторого объекта на плоскости: @@ -564,3 +569,14 @@ class Entity: любое действие можно переопределить при помощи magic методов. [Хорошая статья по теме](https://habr.com/ru/post/186608/) + +Практика: + +1. Создаем класс студент, задаем ему имя и оценки, через __init__ +2. Добавляем метод добавления оценки +3. Добавляем метод вычисления среднего балла +4. Прописываем меджик метод (или методы) которые позволяют найти студента с наилучшим средним балом из списка +5. Создаем класс группы студентов. У группы есть название и список студентов +6. Добавляем возможность добавить студента к группе +7. Добавляем возможность удалить студента из группы +8. Добавляем возможность найти группу в которой учится студент с самым высоким средним баллом \ No newline at end of file