Недавно я написал сообщение о важности начинать с малого, когда вы изучаете новый язык или фреймворк.

В этом посте я предложил кучу крошечных одноразовых экспериментов, которые вы могли бы провести, чтобы отказаться от использования руководств и начать самостоятельное кодирование на новом языке.

Однако ключевой вывод из этого поста был инкрементализм. Вы учитесь, создавая что-то легкое, а затем постепенно усложняя все на один уровень. Расширяйте границы своего понимания, но управляемыми способами.

Простые приложения преподают большие уроки

В этой серии сообщений я хочу проиллюстрировать, что я имею в виду под инкрементализмом.

Мы начнем с создания чего-то тривиального, а затем будем постоянно его усложнять.

Кто знает, чем мы закончим к концу этой серии? Но каждый пост в этой серии должен быть самостоятельным и преподавать хорошие уроки.

Идея состоит в том, чтобы я также расширил границы моего понимания Python и разработки программного обеспечения. Таким образом, не относитесь к этим статьям как к учебным пособиям. Вместо этого думайте об этом как о совместных усилиях. Не стесняйтесь следить за мной, создавать свою собственную версию приложения и предлагать улучшения для моего приложения!

Итак, что мы строим?

Я думаю, что простая игра в крестики-нолики - хорошая отправная точка.

Сама игра должна быть довольно простой в реализации. Но он также предложит всевозможные возможности для будущего развития и сложности, не становясь слишком массивными.

У меня есть всевозможные идеи для возможной игры, включая создание ИИ, который никогда не проигрывает игрокам в крестики-нолики. Звучит весело, правда?

Попутно мы будем использовать git для контроля версий, тестировать приложение с помощью pytest, может быть, портируем его, чтобы играть в онлайн? Вариантов масса!

Но пока нам нужно построить основу игры. Итак, давайте просто создадим простую версию крестиков-ноликов для командной строки.

Начните с очень малого - крошечными шагами!

Цель этого приложения - доказать ценность инкрементализма. Так что не расстраивайтесь, если вы ничего не знаете о тестировании, управлении версиями или развертывании приложений Python в Интернете.

В конце концов мы доберемся до всего этого, но сначала мы начнем с тривиального мини-приложения, которое даже ничего не делает.

Давайте сначала визуализируем доску для игры в крестики-нолики. Вот и все!

Мини-шаг №1: Определите доску

Хорошо, давайте подумаем о доске для игры в крестики-нолики:

Это доска 3x3. Каждое место на доске находится в одном из трех состояний: пустое, X или O.

Итак, давайте пока создадим статическую доску, чтобы мы могли попрактиковаться в ее рендеринге на Python.

ПОДОЖДИТЕ! Прежде чем вы рассмотрите мою идею о том, как мы должны определить доску, подумайте о своей собственной!

Это не учебник. Не копируйте мой код или просматривайте его. Подумайте сами, прежде чем смотреть на то, что я придумал!

Хорошо, вот мое представление о том, как определить доску.

board = [
    ["_", "_", "O"],
    ["X", "O", "_"],
    ["O", "_", "X"]
]

Я создал доску в виде списка списков, чтобы мы могли легко отслеживать строки. Но вы также можете сделать это одним списком.

board = [None, None, "O", "X", "O", None, "O", None, "X"]

Оба работают. Есть компромисс между удобочитаемостью и производительностью во времени / пространстве. Но поскольку это приложение очень маленькое и используется в учебных целях, я решил ошибиться в сторону удобочитаемости.

Я также решил использовать "_" для своих пустых квадратов, но вы можете представить себе другое значение, например None или что-то еще. Причина, по которой я выбрал "_", заключается в том, что он будет хорошо отображаться на консоли. Кроме того, позже будет легко проверить равенство с "_", вместо того, чтобы иметь дело с путаницей проверки равенства с None.

Мы собираемся использовать первую версию со списком списков.

Вот и все! Мы продумали и определили нашу доску! Ваш может отличаться от моего, и это нормально. Собственно в этом весь смысл этой серии.

Мини-шаг №2: Распечатайте доску

Хорошо, мы определили доску. А теперь напечатаем.

Во-первых, давайте посмотрим, как это будет выглядеть, если просто распечатать доску.

# ttt.py
board = [                                                                                                                                                             
    ["_", "_", "O"],                                                                                                                                                  
    ["X", "O", "_"],                                                                                                                                                  
    ["O", "_", "X"]                                                                                                                                                   
]                                                                                                                                                                     
                                                                                                                                                                      
print(board)

Запустить его:

$ python ttt.py
[['_', '_', 'O'], ['X', 'O', '_'], ['O', '_', 'X']]

Хм, это печатная плата, но это не интуитивно понятно. Думаете, мы можем складывать ряды друг на друга?

Мини-шаг: попробуйте вывести каждую строку в консоль на отдельной строке. Не смотрите на мое решение, пока не получите собственное!

# ttt.py                                                                                                                                                              
                                                                                                                                                                     
board = [                                                                                                                                                             
    ["_", "_", "O"],                                                                                                                                                  
    ["X", "O", "_"],                                                                                                                                                  
    ["O", "_", "X"]                                                                                                                                                   
]                                                                                                                                                                     
                                                                                                                                                                     
for row in board:                                                                                                                                                     
    print(row)

Запустить его:

$ python ttt.py
['_', '_', 'O']
['X', 'O', '_']
['O', '_', 'X']

Выглядит хорошо!

Мини-шаг № 3: сделайте его многоразовым

Одним из ключевых элементов разработки программного обеспечения является возможность многократного использования кода.

Возможно, нам придется распечатать доску несколько раз в финальном приложении. Нам не следует повторять один и тот же for цикл везде, где мы хотим распечатать доску.

Кроме того, что, если мы хотим изменить способ печати доски? Если мы повторим цикл for в нескольких местах, нам потребуется внести изменения в нескольких местах.

Намного лучше, если мы определим print_board функцию. Это довольно легко сделать.

Попробуйте!

# ttt.py                                                                                                                                                              
                                                                                                                                                                     
board = [                                                                                                                                                             
    ["_", "_", "O"],                                                                                                                             
    ["X", "O", "_"],                                                                                                                                         
    ["O", "_", "X"]
]      
def print_board(board):                                                                                                                                                                                                                                                                                                                                     
    for row in board:                                                                                                                                                     
        print(row)
print_board(board)

Мини-шаг №4: Составьте список для создания доски

Отлично, мы на пути к игре в крестики-нолики.

Теперь давайте избавимся от нашего образца платы и инициализируем доску в пустом состоянии в начале игры.

Мы могли бы легко сделать что-то вроде этого:

board = [                                                                                                                                                             
    ["_", "_", "_"],                                                                                                                             
    ["_", "_", "_"],                                                                                                                                         
    ["_", "_", "_"]
]

И это отлично сработает.

Но мы стараемся постепенно совершенствоваться в Python. Это создание доски требует понимания списка! Мы можем сделать это в одной строчке кода!

Попробуйте!

board = [["_" for _ in range(3)] for _ in range(3)]

Обратите внимание, как я использовал _. Если вы никогда не видели этого раньше, в Python обычным способом является создание цикла, когда нам фактически не нужен доступ к самому итератору.

Среди прочего, это одноразовая ценность. На самом деле, чтение этого ответа на stackoverflow научило меня другим формально определенным видам использования подчеркивания в Python, о которых я действительно не знал! Учимся постепенно!

Бонусные очки:

Вы могли подумать,

«Эй, погоди! Если мы сможем составить список, чтобы создать доску, не могли бы мы также распечатать доску в одну строку? »

И вы правы, если так думаете! Технически мы можем распечатать доску в одну строку, используя понимание списка:

def print_board(board):
    [print(row) for row in board]

Но мне было любопытно, действительно ли это хорошая идея? Похоже, что понимание списка может добавить накладные расходы к операторам печати, создавая ненужный список.

Так что - всегда учись постепенно - я это тестировал!

# ttt.py
import timeit
board = [["_" for _ in range(3)] for _ in range(3)]
def print_board_list(board):
    [print(row) for row in board]
def print_board_for(board):
    for row in board:
        print(row)
begin = timeit.default_timer()
print_board_list(board)
end = timeit.default_timer()
print(f"Using list comprehension: {end - begin}")
begin = timeit.default_timer()
print_board_for(board)
end = timeit.default_timer()
print(f"Using a for loop: {end - begin}")

И оказывается, что использование списка в этом случае в среднем примерно в два раза медленнее:

$ python ttt.py 
['_', '_', '_']
['_', '_', '_']
['_', '_', '_']
Using list comprehension: 0.00018204300431534648
['_', '_', '_']
['_', '_', '_']
['_', '_', '_']
Using a for loop: 9.22030012588948e-05

(Обратите внимание на научное обозначение e-05 в конце, если вы запутались.)

Итак, это отличный пример того, что написание однострочных строк - это чрезмерное занятие. Это действительно замедляет нашу программу.

Честно говоря, мы говорим о нескольких сотых тысячных секунды в игре в крестики-нолики. Если вы изучаете понимание списка, это может быть интересно реализовать, а разница в скорости безвредна.

Тем не менее, мне было весело протестировать и рассчитать оба подхода! Я узнал что-то постепенно и раздвинул границы своего понимания.

Ознакомьтесь с кодом теста скорости на GitHub

Подводя итоги на сегодня

Может быть, кажется, что сегодня мы мало что сделали. Если бы это был учебник, вы были бы разочарованы. Все, что мы сделали, это составили список списков и затем распечатали его.

Но я чувствую, что мы многому научились!

У меня достаточно опыта разработки Python, и я даже научился чему-то с помощью этих простых упражнений.

В этом весь смысл! Создайте что-нибудь, что вы знаете, и поднимите это на новый уровень.

Вы можете найти сегодняшний код на GitHub.

В следующем посте серии мы начнем экспериментировать с принятием пользовательского ввода, обнаружением ошибок и обновлением доски.



Серия« Крестики-нолики : получение и проверка данных, вводимых пользователем в Python»
Вчера я начал серию статей о постепенном изучении Python и разработке программного обеспечения, используя крестики-нолики в качестве … medium.com »



Ознакомьтесь с полным списком всех постов из этой серии про крестики-нолики.



Понравилось то, что вы здесь прочитали?

Я бесплатно делюсь своим лучшим контентом со своим списком рассылки.

Присоединяйтесь к 500 другим разработчикам в моей серии писем.