Перейти к содержанию

Основы программирования

Компьютерные науки в университете · Тема 1

1.1

О курсе и способе его использования

Это первый курс по университетской информатике: основы программирования , преподаётся с использованием Python 3. Не требует предварительных знаний программирования. Каждая программа в в этом раздаточном материале завершена и готова к запуску — наберите её, запустите, измените одну строку и предскажите что изменится перед повторным запуском. Цикл «предсказание-запуск» составляет всю суть навыка.

Курс представляет собой лестницу:

  • Модули 1.1 и 1.3 формируют основы механики: значения и типы, выборка, итерация.
  • Модуль 1.4 упаковывает работу в функции.
  • Модули 1.5 и 1.6 добавляют стандартные коллекции и файлы с обработкой ошибок.
  • Модуль 1.7 — это дисциплина верха: декомпозиция задач, тестирование, отладка.

Как читать каждый модуль: короткая история объясняет, почему это важно; картинка или правило идут до любого синтаксиса; затем точные правила; затем проработанные примеры, становящиеся всё сложнее, каждый со следом, который можно проверить, запустив код; затем ошибки, которые люди на самом деле допускают; затем трёхстрочное резюме. Каждый пример здесь можно проверить вторым способом — запустив его. Чтение кода без запуска похоже на чтение нот без того, чтобы услышать музыку.

1.1

Значения, типы и ввод/вывод

Приложение для покупок показывает "4.20" — это текст или число? Если это текст, сложение двух цен даст "4.203.50"; если это число, оно даст 8.7. Что именно произойдёт, решает тип, и весь модуль 1.1 посвящён тому, чтобы учиться видеть типы до того, как вы запустите что-либо.

Значение имеет тип, а тип определяет, что с ним можно делать: int (целые числа), float (десятичные), str (текст), bool (True/False). Функция type(...) называет тип любого значения — задавайте её свободно во время обучения.

age = 21              # int
price = 4.20          # float
name = "Ada"          # str
open_ = True          # bool
print(type(age))      # <class 'int'>

Переменные — это имена, привязанные к значениям; присваивание age = 22 перепривязывает имя — оно никогда не редактирует старое значение. Ментальная модель, которую стоит сохранить на протяжении всей учёбы:

Состояние программы в виде блоков: каждая метка имени указывает на собственный блок значения; переназначение перемещает метку, но никогда не редактирует сам блок. Выражение 6 * 3.50 считывает два блока и создает новый блок float 21.0
Имена — это ярлыки на коробках; выражения читают из коробок и создают новые

Вычисление выражения — это «открыть коробки, вычислить, закрыть новую коробку»: count * 3.50 при count = 6 открываются коробка int 6 и коробка float 3.50, а Python выполняет смешивание правило повышает разрядность int — результатом становится float 21.0, никогда не int 21.

Превращение явное: int("21") является 21, но "4" + 2 — это TypeError — Python отказывается угадывать, имели ли вы в виду "42" или 6. Выводите с помощью print(...); вводите с input(...), который всегда возвращает строку, поэтому числа нужно преобразовывать:

born = int(input("Birth year? "))
print("About", 2026 - born, "years old")

Разобранное решение 1 (кассовый аппарат кафе). count = int(input("Coffees? ")), затем print("Total:", count * 3.50). При 6 кофе этот код выведет Total: 21.0 — это .0 помечает число с плавающей точкой, потому что int * float повышает разрядность до float. Отслеживание того, какой тип получается на выходе из выражения, составляет основную привычку unit 1.1. Проследите его по состоянию: input возвращает строку "6"; int(...) открывает новую коробку, содержащую int 6; умножение создаёт свежую коробку float со значением 21.0; print отображает её.

Разобранный пример 2 (классическая ошибка, исправленная). Принтер баннеров пытается сложить счётчик со строкой:

count = int(input("Tickets? "))
print("Sold " + count + " today")

Запустите его, и Python выдаст TypeError: can only concatenate str (not "int") to str — правило отказывается гадать. Исправление явно указывает преобразование: print("Sold " + str(count) + " today"), или более чистый вариант print("Sold", count, "today"), which inserts the spaces for you. The lesson to carry: + означает сложение для чисел, конкатенацию для строк и никогда не тихое смешивание.

Типичные ошибки. Забывание о том, что input() всегда возвращает строку, поэтому input() * 2 повторяет текст, а не удваивает число. Написание 4.20 + 6 и ожидание ответа в формате int — любое дробное число в выражении делает результат дробным. Доверие 0.1 + 0.2 == 0.3 (это False; двоичное округление — единица 1.2 показывает исправление).

Резюме: четыре базовых типа определяют каждую операцию; имена — это метки, присваивание перепривязывает; выражения смешивают типы с помощью приведения через числа с плавающей точкой; преобразования выполняются явно и input() требует одного.

English Русский
programming fundamentals/ˈprəʊɡræmɪŋ ˌfʌndəˈmentlz/ Основы программирования
type/taɪp/ тип
set/set/ множество
1.2

Выбор: выбор между путями

Турникет в парке развлечений должен взимать плату по-разному для детей, взрослых и пожилых людей — одна программы, несколько путей. Выбор — это способ, которым программа принимает решения: if, elif, else выполняет блок только при выполнении условия.

Схема алгоритма правил цен в кинотеатре с проверкой возраста перед проверкой статуса студента; студент возраста 70 попадает в первое истинное условие и платит 5
Возраст 70, студент: первый истинный ветвь определяет цену

Условием является любое логическое выражение; сравнения (==, !=, <, >=) и логика (and, or, not) его формируют. Два правила делают каждое решение читаемым:

  1. Ветви проверяются сверху вниз; первая истинная ветвь выигрывает, остальные пропускаются.
  2. Отступы — это не просто оформление — они И есть блок. Один лишний пробел меняет, какие строки относятся к ветви.
temp = 37.5
if temp >= 38:
    print("Fever")
elif temp >= 36 and temp < 38:
    print("Normal")
else:
    print("Low")

Разобранный пример 1 (цена за вход в кино). При 12 или 65+ плата составляет 5, студенты платят 7, всем остальным 10:

age = int(input("Age? "))
student = input("Student? y/n ") == "y"
if age < 12 or age >= 65:
    price = 5
elif student:
    price = 7
else:
    price = 10
print("Pay", price)

Порядок имеет значение: побеждает первая истинная ветвь, поэтому проверка возраста предшествует проверке студента — 70-летний студент должен платить 5, и именно такой порядок это обеспечивает. Запустите код со значениями 70 и y, чтобы увидеть, что срабатывает левый путь на блок-схеме.

Разбор примера 2 (диапазоны оценок, оптимизированный). Тот же каркас, но теперь в виде цепочки условий, где каждое elif требует всего одного сравнения, поскольку предыдущие проверки уже не прошли:

score = int(input("Score? "))
if score >= 80:
    band = "A"
elif score >= 70:
    band = "B"
elif score >= 60:
    band = "C"
else:
    band = "F"
print("Band:", band)

На уровне 79 первое условие не выполняется, второе выполняется: результат Band: B. Переход к заметке: для проверки elif score >= 70 также не нужно проверять and score < 80 — достижение этой строки уже доказывает, что балл ниже 80. Доверие механизму последовательного выполнения (fall-through) в этом случае делает цепочки короткими; повторная проверка всех условий — это более длинная и неверная версия для новичков. версия.

Сравнение чисел с плавающей точкой требует осторожности: 0.1 + 0.2 == 0.3 может быть равно False из-за округления в двоичной системе округлению), поэтому сравнивайте с допустимой погрешностью: abs(x - y) < 1e-9.

Распространенные ошибки. Написание = там, где подразумевалось == — классическая ошибка, и Python запрещает её в условиях (SyntaxError) именно потому, что она встречается слишком часто. Проверка частного случая после общего, из-за чего общее условие всегда выигрывает. Использование elif с условием, которое никогда не будет достигнуто, так как более ранняя ветвь уже обработала этот случай.

Резюме: условия выбирают пути по принципу «первая истинная побеждает», отступ определяет блок кода; порядок тесты проверяют от частного к общему; числа с плавающей точкой сравниваются через допустимую погрешность, никогда не по ==.

1.3

Итерация: повторение действия

Обработка 5,000 счетов вручную — это работа на всю жизнь; с помощью цикла это программа. Итерация повторяет работу: цикл for проходит по известной последовательности, а цикл while выполняется, пока выполняется условие. Главный вопрос при проектировании всегда таков: знаете ли вы точное количество шагов или же условие определяет завершение?

total = 0
for i in range(1, 101):      # 1..100 — range stops BEFORE its end
    total += i
print(total)                  # 5050

Используйте for, когда количество шагов известно; используйте while, когда условие решает об остановке. Для каждого while требуются три элемента, видимые в коде: начальное значение переменной, само условие и действие внутри цикла, которое приближает к выходу из него — иначе это бесконечный цикл. цикл**.

While-цикл факториала рядом с таблицей трассировки: n и factorial после каждого раунда, от 5 и 1 до 1 и 120, когда условие становится False
Таблица трассировки для факториального цикла
n = 5
factorial = 1
while n > 1:
    factorial *= n
    n -= 1                    # the step toward exiting
print(factorial)              # 120

Таблица трассировки — это обязательная практика курса для отладки циклов: прослеживайте переменные по кругу за каждую итерацию, сначала на бумаге. Для факториального цикла: (n=5, f=1) → (4, 5) → (3, 20) → (2, 60) → (1, 120). Составьте таблицу вручную, затем добавьте print(n, factorial) внутрь цикла и позвольте Python проверить вас.

Разобранное решение 2 (поиск с использованием break, усложнённый вариант). Найдите первое число, кратное 7, которое больше 100 — количество шагов неизвестно, условие определяет завершение:

n = 100
while True:
    if n % 7 == 0:
        break                 # found it — leave the loop now
    n += 1
print(n)                      # 105

Трассировка короткая: 100, 101, …, 104 все проходят проверку; 105 проходит, а break прерывается. Оператор break досрочно покидает цикл; оператор continue пропускает текущую итерацию и переходит к следующей. Оба являются средства читаемости кода — цикл, который должен сканировать данные, пока что-то не найдёт, затем остановится, читает лучше всего с break. Дальнейшее наращивание, удвоение денег: под ставку 5% годовых, сколько времени берёт ли 1000 для достижения 2000?

balance = 1000.0
years = 0
while balance < 2000.0:       # the condition itself is the goal
    balance *= 1.05
    years += 1
print(years, round(balance, 2))   # 15 2078.93

Пятнадцать лет, и цикл останавливается в момент изменения условия — таблица трассировки показала бы пересечение баланса через отметку на 2078.93, а не точное попадание на 2000.

Распространённые ошибки. Изменение списка (модуль 1.5) или переменной цикла, по которой вы for-итерируете. range(1, 100) при условии 100 — конец не включён. Цикл while без шага выхода или с закомментированным шагом: условие никогда не меняется, и программа зависает.

Резюме: for для известного количества циклов, while для условий; каждый цикл имеет начало, условие и шаг; трассировка на бумаге, затем вывод; break и continue для чистого раннего выхода.

1.4

Функции: определение задачи одним именем

Одна и та же расчётная формула цены встречается в пяти местах; правило налога меняется; четыре из пяти обновляются. Функция упаковывает задачу под имя, чтобы она была написана один раз и вызывалась везде: параметры на входе, возвращаемое значение на выходе. def определяет; вызов выполняет.

def total_price(count, unit=3.50):
    """Return the total for count items at unit price."""
    return count * unit

print(total_price(6))            # 21.0
print(total_price(6, unit=4.0))  # 24.0

Определение задаёт параметр (count, unit); вызов передаёт аргумент (6). Значение по умолчанию (unit=3.50) делает аргумент необязательным. return возвращает значение и завершает вызов; вывод на экран — это не возврат: функция без return возвращает None, а вызывающий код, пытающийся выполнить над ним арифметику, получает ошибку TypeError.

Три вызова функций: print вызывает order_total(250, 4), который приостанавливается, пока выполняется add_fee(1000); 1003 возвращается по цепочке
Вложенные вызовы: вызывающая функция приостанавливается до получения значения обратно

Каждый вызов получает собственную локальную область видимости: имена внутри неё невидимы снаружи и исчезают после завершения вызова. Трассировка вложенных вызовов выглядит так: вызывающий приостанавливается → вызываемый работает → возвращаемое значение попадает к вызывающему.

Разобранный пример 1 (заказ с учётом комиссии).

def add_fee(amount):
    """Return amount plus a fixed 3 pence fee."""
    return amount + 3

def order_total(unit_pence, quantity):
    """Return the order total: subtotal plus the fixed fee."""
    return add_fee(unit_pence * quantity)

print(order_total(250, 4))   # 1003

Контракт в docstring имеет значение: целые пенни на входе, целые пенни на выходе. Трассировка order_total(250, 4): subtotal 1000, add_fee возвращает 1003 — и кадр диаграмма выше показывает паузу и передачу управления.

Разобранный пример 2 (реальный алгоритм внутри небольшой функции, расширенный). is_prime(n) возвращает True, если ничего из 2 до $\sqrt{n}$ не делит n:

def is_prime(n):
    if n < 2:
        return False
    d = 2
    while d * d <= n:        # only test up to the square root
        if n % d == 0:
            return False     # found a divisor — answer known, stop
        d += 1
    return True

print([p for p in range(2, 30) if is_prime(p)])
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Здесь встречаются две особенности работы функций: ранний return return завершает вызов, как только ответ становится известен (переменная-флаг не нужна), а граница цикла d * d <= n берётся из вывода — если для $n = a \times b$ подходит $a \le b$, то и для $a \le \sqrt{n}$, поэтому проверка за пределами $\sqrt{n}$ проверяет делители, которые уже были проверены. Запустите код: список содержит ровно простые числа меньше 30.

Типичные ошибки. Печать результата внутри функции вместо его возврата, из-за чего результат нельзя использовать повторно. Забытый return return на одной из ветвей — этот путь молча возвращает None. Попытка доступа к локальным переменным функции снаружи; они недоступны после завершения вызова.

Резюме: return def один раз, вызовите где угодно; параметры поступают, одно значение выходит через return return; ранний возврат завершает вызов; локальные переменные существуют и уничтожаются вместе с вызовом; строки документации описывают контракт.

English Русский
string/strɪŋ/ строка
selection/sɪˈlekʃn/ выбор
iteration/ˌɪtəˈreɪʃn/ Итерация
infinite loop/ˈɪnfɪnət luːp/ бесконечный цикл
trace table/treɪs ˈteɪbl/ таблица отслеживания
function/ˈfʌŋkʃn/ функция
return value/rɪˈtɜːn ˈvæljuː/ Возвращаемое значение
parameter/pəˈræmɪtə/ Параметр
argument/ˈɑːɡjuːmənt/ фактический аргумент
local scope/ˈləʊkl skəʊp/ Локальная область видимости
list/lɪst/ список
dictionary/ˈdɪkʃənəri/ словарь
tuple/ˈtuːpl/ кортеж
1.5

Коллекции: списки, словари, кортежи, множества

Касса кафе требует заказы дня в правильном порядке, меню по названиям, местоположение, которое не должно смещаться, и уникальные напитки сегодняшнего дня. Четыре задачи, четыре контейнера — выбор среди них есть архитектурное решение — единица измерения 1.5.

Список — это упорядоченная, изменяемая последовательность: marks = [72, 85, 61], нумерованная начиная с 0, срезаемая через marks[1:3]. Словарь связывает ключи со значениями: price = {"flat white": 3.5, "tea": 2.8}, читаемый и записываемый по ключу. Кортеж это фиксированная последовательность (53.1, -1.5) — подходит для координат, которые не должны изменяться. A множество хранит уникальные элементы без порядка: {"a", "b"}.

marks = [72, 85, 61]
marks.append(90)               # [72, 85, 61, 90]
best = max(marks)              # 90
average = sum(marks) / len(marks)   # 77.0
Имена a и b указывают на один объект списка, содержащий 1 и 2 после выполнения b.append(2)
Алиасинг: два имени, один список

Присваивание не копирует: a = [1]; b = a; b.append(2) делает a [1, 2] — список является общим, имена — нет. Этот алиасинг — снова коробки из модуля 1.1, теперь с двумя тегами в одной строке, а также объясняет следующее правило: мутация списка во время прохода по нему пропускает элементы — пройдите по копии (for m in marks[:]) или создайте новый список.

Разобранный пример 1 (подсчёт по категориям).

orders = ["tea", "flat white", "tea", "tea", "mocha"]
tally = {}
for item in orders:
    tally[item] = tally.get(item, 0) + 1
print(tally)   # {'tea': 3, 'flat white': 1, 'mocha': 1}

Метод словаря .get(key, 0) возвращает 0 при первом появлении — этот приём делает подсчёт однострочником. Проверьте вручную: слово "tea" встречается три раза в orders, и напечатанный счёт это подтверждает.

Разобранный пример 2 (лучший хит дня, усложнённый). Опираясь на подсчёт, укажите самый частый элемент и его количество:

best = max(tally, key=tally.get)
print(best, tally[best])       # tea 3

Аргумент key=tally.get заставляет max сравнивать элементы по их частоте появления, а не по алфавиту — тот же приём key= сортирует любую коллекцию по любому критерию. Усложнение важно: реальные программы цепляют эти инструменты вместе (подсчёт, затем ранжирование), вместо того чтобы подчеркивает важность последовательности операций: реальные программы цепляют эти инструменты вместе (сначала подсчёт, затем ранжирование), а не сканируют orders дважды.

Выбор типа коллекции — это архитектурное решение: порядок + дубликаты → список; поиск по имени → словарь; фиксированная группировка → кортеж; уникальность → множество. имя → словарь; фиксированная группировка → кортеж; уникальность → множество.

Предположение, что tally[item] += 1 копирует список; на самом деле он создаёт ссылку на него. (KeyError) — .get является исправлением. Ожидание того, что множество или словарь сохранят порядок. делает подсчёт безопасным; b = a превращает ранжирование в однострочник.

Резюме: четыре контейнера для четырех задач; назначение делит, никогда не копирует; .get делает подсчет безопасным; key= делает ранжирование однострочником.

English Русский
aliasing/ˈeɪlɪəsɪŋ/ алиасирование
1.6

Файлы и ошибки

Кафе закрывается, свет гаснет — а утренний подсчет выживает только если программа записала его на диск. Файл существует дольше программы. with open(path) as f: opens, closes on exit, and reads text; open(path, "w") записывает (и сначала очищает — классическая ошибка).

with open("log.txt", "w") as f:
    f.write("opened\n")

with open("log.txt") as f:
    for line in f:
        print(line.rstrip())     # rstrip removes the trailing newline

Блок with — это блок безопасности: что бы ни произошло внутри него, файл будет закрыт. Разобранный пример .rstrip() (отсутствующий файл настроек). Чтение размера текста из файла настроек, который может отсутствовать или содержать не целое число:

Ошибки — это значения, которые Python выбрасывает как исключения: FileNotFoundError, ValueError, KeyError, TypeError. try/except перехватывает именованный тип — никогда не голый except:, который скрывает реальные ошибки.

Разобранный пример 1 (отсутствующий файл настроек). Чтение размера текста из файла настроек, который может отсутствовать или не содержать целое число:

try:
    with open("settings.txt") as f:
        size = int(f.read().strip())
except FileNotFoundError:
    size = 12                    # no file yet: a sensible default
except ValueError:
    size = 12                    # the file holds "big", not a number
print("Text size", size)

Запустите его три раза — со 16 в файле, без файла и со big в файле — и он каждый раз выводит корректный размер из трёх разных путей. Это и есть форма надёжного кода: одна разумная результат, несколько дорог к нему. Обрабатывайте ожидаемую ошибку там, где вы можете отреагировать на нее; позвольте остальным аварийно завершить работу.

Читатель настроек: блок try открывает, читает и преобразует значение; всё работает либо ветка FileNotFoundError или ValueError устанавливает размер по умолчанию 12; все три пути сходятся, и программа продолжает работу

**Разбор примера 2 (надежный цикл ввода, усложненный). Спрашивайте до тех пор, пока пользователь не введет целое число:

def read_count():
    while True:
        try:
            return int(input("How many? "))
        except ValueError:
            print("Whole numbers only.")

print(read_count())

Передайте ему x, затем 4: первый проход вызывает исключение, перехватывается, выводит сообщение, а while True запрашивает снова; второй проход успешно выполняет int(...), и return выходит из цикла и функции за один шаг. Здесь объединяются три элемента: цикл (1.3), контракт функции (1.4) и исключение (1.6) — именно в этом заключается суть усложнения.

Типичные ошибки. open(path, "w") на неверном пути: файл создается или очищается еще до того, как вы заметите проблему. Перехватывать Exception слишком широко и продолжать выполнение с состояние. Забывание .rstrip(), поэтому вывод отображается с двойными пробелами.

Краткое содержание: with open(...) для чтения и письма; "w" очищает первым; исключения — это значения; ловите ожидаемый тип по имени, делайте разумный откат, позволяйте остальному упасть.

English Русский
value/ˈvæljuː/ значение
variables/ˈveərɪəblz/ Переменные
file/faɪl/ файл
exceptions Исключения
decomposition/ˌdiːkɒmpəˈzɪʃn/ декомпозиция
testing/ˈtestɪŋ/ тестирование
debugging/ˈdiːbʌɡɪŋ/ отладка
1.7

Декомпозиция, тестирование и отладка

Две команды пишут одно приложение; одна выпускает его за неделю, другая тонет месяц. Разница не в скорости печати — дело в том, что код быстрой команды состоит из небольших кусков, каждый из которых можно проверить на корректность отдельно, тогда как медленная команда одновременно отлаживает всё приложение. Юнит-1.7 — это дисциплина.

Декомпозиция разбивает задачу на функции по одной задаче каждая — каждая небольшая достаточно для отдельного тестирования. Тестирование сравнивает фактический выход функции с ожидаемым, на границах: ноль, один, пустой, отрицательный, огромный.

Цикл, управляемый тестом: напишите падающий тест, который описывает контракт; запустите его и убедитесь, что он упал по правильной причине; напишите самое простое тело, которое проходит; перезапускайте до зеленого состояния; рефакторьте, пока тесты удерживают форму
Цикл, управляемый тестом: красный, зеленый, рефакторинг
def add_fee(amount):
    return amount + 3

def order_total(unit_pence, quantity):
    return add_fee(unit_pence * quantity)

assert order_total(250, 4) == 1003
assert order_total(250, 0) == 3      # the zero-quantity boundary
assert order_total(0, 4) == 3

Юнит-assert, который никогда не падает, — это успешный тест. Пишите тесты до написания тела, когда возможно — они являются контрактом, переведенным в исполняемый код, а цикл выше (красный → зеленый → рефакторинг) — это привычка, работающая непрерывно.

Разобранный пример 1 (расширение тестов вместе с функцией). Расширьте order_total с помощью 10% скидки сверх £10 — тест пишется сначала, на новой границе:

def order_total(unit_pence, quantity):
    subtotal = add_fee(unit_pence * quantity)
    if subtotal >= 1000:             # pence: over £10
        subtotal = int(subtotal * 0.9)
    return subtotal

assert order_total(200, 4) == 803    # under £10 — unchanged
assert order_total(250, 4) == 902    # 1003 pence → 10% off → 902.7 → 902
assert order_total(250, 5) == 1127   # 1253 pence → 1127.7 → 1127

Проверьте средний тест вручную: subtotal 1003, скидка в десять процентов дает 902.7, и int(...) округляется вниз до 902. Обратите внимание на первый assert также: при 200 пенни и четырёх чашках общая сумма остаётся 803 — граничные тесты доказывают, что скидка применяется и что она не срабатывает преждевременно.

Разобранный пример 2 (бисекция бага с помощью print, усложнённая). Подсчёт из 1.5 выводит {'tea': 3}, но строка бестселлера печатает неправильный напиток. Метод: (1) воспроизвести с наименьшим входящим, вызывающим ошибку; (2) распечатать значения на границе между доверенной частью и подозрительной частью — здесь, подсчёт перед max; (3) исправить одну вещь; (4) перезапустить тесты. Перемещение print посередине кода на каждом запуске сокращает область поиска вдвое: четыре print находят баг в программе из шестнадцати строк, однако каждый запуск сокращает поиск вдвое: четыре вывода находят баг в программе из шестнадцати строк, однако хорошо бы он ни скрывался.

Отладка — это поиск различия между тем, что вы считаете, делает код, и тем, что он делает на самом деле; print на границе доверия/подозрения является инструментом для этого. и то, что он делает — вывод на границе доверия/подозрения является инструментом.

Типичные ошибки. Тестировать только «счастливый путь»: именно на границах (ноль, один, пустое, отрицательное) программы ломаются. Писать неделю кода до первого теста, отрицательные) — это места, где программы ломаются. Писать неделю кода до первого теста, затем отлаживать гору. Исправлять две вещи одновременно, так что ни один результат не имеет смысла. чем-либо.

Резюме: декомпозируйте до тех пор, пока каждый компонент тестируется отдельно; assert-ируйте границы сразу же после создания функции; пишите тест заранее, когда это возможно; отлаживайте методом бисекции с print на границах доверия — это навык, на котором строится вся программа обучения. функция существует; пишите тест первым, когда это возможно; отлаживайте бисекцией с помощью print на границах доверия — это навык, на котором строится вся учебная программа.

Интерактивные уроки по этой теме

Пройдите его шаг за шагом с упражнениями мгновенной проверки.

Больше тем в Компьютерные науки в университете

Войти или создать аккаунт

IGCSE, A-Level & AP