Чем больше вы практикуетесь, тем легче программирование становится для вас, но суть не меняется:
программирование — это сложно.
Оно может стать ещё сложнее при неудачном сочетании обязательств и рабочих задач, с которыми вы справляетесь самостоятельно. Без наставника особенно сложно осознать, что применяемый вами способ неверен. Конечно, мы все возвращаемся к коду и рефакторим его спустя некоторое время, потому что постоянно учимся делать что-то лучше. К счастью, при должном уровне осведомлённости работа над этими ошибками повышает ваш уровень программирования.
Преодолевать проблемы и работать над ошибками — замечательный способ стать хорошим программистом. Всегда существует лучший способ сделать что-то, но найти этот способ бывает действительно сложно. Легко привыкнуть к тому или иному действию, и иногда нужна небольшая встряска, чтобы двигаться дальше.
NotImplemented
Хотя ошибка NotImplemented является одной из самых распространённых, я считаю важным напомнить о ней в очередной раз. Выброс NotImplemented в Python не приводит к выбросу ошибки NotImplemented, вместо этого выбрасывается TypeError. Вот пример:
def implementtest(num):
if num == 5:
raise(NotImplemented)
При любой попытке запустить функцию, где “num” равен 5, произойдёт следующее:
Для выброса корректного исключения стоить выбрасывать NotImplementedError вместо NotImplemented. Отредактируем функцию:
def implementtest(num):
if num == 5:
raise(NotImplemented)
if num == 10:
raise(NotImplementedError('This is the right way!'))
Выполнение этой функции даст нужный результат:
Изменяемые значения по умолчанию
(Каюсь, и я допускал эту ошибку).
Аргументы по умолчанию в Python задаются один раз при определении функции. Следовательно, каждый элемент используется при каждом вызове, то есть данные, содержащиеся в переменной, изменяемы при каждом обращении к ним в функции.
def add(item, items=[]):
items.append(item)
Вместо этого стоит задать нулевое значение параметрам и добавить условие для изменения списка, если его не существует.
def add(item, items=None):
if items is None:
items = []
items.append(item)
Хотя в основном это относится к пользователям Python, работающим со статистикой, данными или в машинном обучении, наличие неизменяемых данных универсально важно во многих обстоятельствах.
Глобальные переменные
Внутри объектно-ориентированного языка программирования глобальные переменные стоит сводить к минимуму. Тем не менее я считаю важным отметить, что глобальные переменные, безусловно, необходимы и вполне применимы в некоторых ситуациях. Замечательным примером служит наука о данных, где фактически происходит ограничение объектно-ориентированного программирования, и Python становится более функциональным, нежели обычно.
При использовании глобальных переменные могут возникать проблемы с именованием и приватностью, когда несколько функций обращаются или ссылаются на одно и то же значение. Прекрасным примером подходящего использования глобальной переменной может быть что-то вроде пути к файлу, особенно предназначенного для объединения в пакет с файлом Python. Даже при написании класса Gtk и перемещении компоновщика графического пользовательского интерфейса стоит делать это частно, а не глобально.
Копирование!
Использование копирования может быть объективно лучше обычного присвоения. Обычные операции присвоения просто указывают на существующий объект новой переменной, вместо создания нового объекта.
d = 5
h = d
Существуют два основных типа копирования, осуществляемых с помощью модуля копирования Python:
поверхностное копирование и глубокое копирование.
Разница между этими двумя типами заключается в типе переменной, передаваемой через функцию. При использовании глубокого копирования переменных, содержащихся в одном байте данных, таких как целые числа, числа с плавающей точкой, булёвые значения или строки, разница между двумя типами копирования не ощущается. Однако при работе со списками, кортежами и словарями я всегда рекомендую глубокое копирование.
Поверхностное копирование создаёт новый составной объект и затем (насколько это возможно) вставляет в него ссылки на объекты, найденные в исходном. Глубокое копирование создаёт новый составной объект и затем рекурсивно вставляет внутрь него копии объектов из исходного. Учитывая эти определения легко понять, почему к тому или иному типу данных больше подходит тот или иной вариант.
import copy
l = [10,15,20]
h = 5
hcopy = copy.copy(h)
z = copy.deepcopy(l)
Чтобы протестировать наши результаты, просто проверим, является ли id переменных тем же самым, что и в условном выражении:
print id(h) == id(hcopy)
False
Заключение
Быть хорошим программистом означает постоянно совершенствоваться и постепенно искоренять ошибочные концепции. Это болезненный процесс, но постоянная практика и следование простым рекомендациям и советам приносят пользу. Обсуждения в стиле “не делайте так” приводят к продуктивному разговору и повышает уровень программистов, и я полагаю, что это обсуждение будет полезным независимо от того, насколько далеко вы продвинулись в своём бесконечном путешествии в программировании.
Читайте также:
- Стоит ли заменить Python на Julia?
- 4 ситуации из жизни лямбда-функций в Python
- Топ-10 магических команд в Python, которые повысят вашу продуктивность
Перевод статьи Emmett Boudreau: Avoid These Rookie Python Mistakes