Несколько рекомендаций

Об особенностях языка Python

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

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

Структура программы

В начале программы идут импорты разнообразных модулей.

Не рекомендуется захламлять пространство имён импортами вида

from foobar import *

или даже

from foobar import foo, bar

Если не нравится слишком длинное имя модуля, то гораздо лучше этот модуль переименовать, чем импортировать его функции без префикса:

import sqlite3 as db

Программу рекомендуется составлять из определений констант и функций, придерживаясь следующих пунктов:

  • следует выбрать единую схему именования сущностей и её придерживаться; например, принято имена глобальных переменных писать исключительно заглавными буквами, а имена функций — змеиным регистром (do_such_and_such)
  • не следует пользоваться возможностью изменения привязок глобально объявленных переменных; про конструкции global и nonlocal лучше вообще забыть
  • привязывать мутабельные объекты к глобальным переменным тоже следует с большой осторожностью
  • желательно вне определений функций не иметь никакого кода, кроме, возможно, единственного вызова функции, которая служит точкой входа в программу

Функции

Разбиение программы на функции с чётко очерченными задачами и регламентом использования позволяет обратить длинную последовательность команд (которой, собственно, программа на Python и является) в гораздо более легко воспринимаемую форму. Сравните, например, такую реализацию сортировки «пузырьком»:

n = int(input()) to_sort = [] i = 0 while i < n: to_sort.append(int(input())) i += 1 i = 0 while i < n: j = 0 while j < n-1: if to_sort[j+1] < to_sort[j]: to_sort[j], to_sort[j+1] = to_sort[j+1], to_sort[j] j += 1 i += 1 i = 0 while i < n: print(to_sort[i], end=' ') i += 1 print()

с такой:

def main(): to_sort = read_array() bubble_sort(to_sort) print_array(to_sort) def read_array(): n = read_int() return [read_int() for i in range(n)] def read_int(): return int(input()) def bubble_sort(array): n = len(array) for i in range(n): bubble_step(array) def bubble_step(array): n = len(array) for j in range(n-1): if array[j+1] < array[j]: swap(array, j, j+1) def swap(a, i, j): a[i], a[j] = a[j], a[i] def print_array(a): n = len(a) for i in range(n): print(a[i], end = ' ') print_newline() def print_newline(): print() main()

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

Выделять код в отдельную функцию рекомендуется как минимум в следующих случаях:

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

Комментарии

При комментировании программы важно помнить, что почти всегда правильный выбор имён и/или удачная функция-тест гораздо полезнее комментария на естественном языке, который:

  • имеет тенденцию устаревать, не успевая за изменениями кода
  • из-за особенностей естественных языков может иметь множество разнообразных интерпретаций

Ни в коем случае не нужно делать комментарии в духе:

a += 3 ## прибавляем тройку

А вот пример более удачного комментария:

x1 = average(distribution, segment) + 3 ## прибавляем тройку ... x2 = approximate('quadratic', foobar, x1) x3 = adjust(y, x1, x2) ## ... поскольку в статье [Х. Суньвчай, И. Выньсухим, 1994] так опредён ## первый шаг аппроксимационного алгоритма

или

foobar(a+3) ## прибавляем тройку для большей надёжности

Говоря другими словами, комментарий должен отвечать не на вопрос «Что тут происходит?» (на этот вопрос отвечает сам код), а на вопрос «Зачем так происходит?»