10 ошибок, которые выдают новичков в Python

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

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

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

1. Использование import *

У ленивого разработчика часто возникает соблазн импортировать все из модуля, используя команду from xyz import *.

Это не очень хорошая практика по многим причинам. Вот лишь некоторые из них.

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

Как с этим бороться? Импортируйте либо конкретный объект, который планируете использовать, либо весь модуль.

# Использование import *

# Плохо
from math import *

print(floor(2.4))
print(ceil(2.4))
print(pi)

# Хорошо
import math
from math import pi

print(math.floor(2.4))
print(math.ceil(2.4))
print(pi)

2. Использование try/except без определения типа исключения в блоке except

Долгое время я пренебрегал этим моментом. Трудно даже сказать, сколько раз Pycharm сообщал (с помощью волнистых линий), что нельзя использовать чистое except. Это не рекомендуется в руководстве PEP 8.

# Использование Try - except
# Плохо
try:
driver.find_element(...)
except:
print("Which exception?")

# Хорошо
try:
driver.find_element(...)
except NoSuchElementException:
print("It's giving NoSuchElementException")
except ElementClickInterceptedException:
print("It's giving ElementClickInterceptedException")

В результате использования чистого except будут перехватываться исключения SystemExit и KeyboardInterrupt, что затруднит прерывание программы с помощью Control-C.

Чтобы избежать этой проблемы, при использовании try/except, определяйте тип исключения в блоке except.

3. Пренебрежение Numpy при математических вычислениях

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

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

Представьте, что у вас есть массив random_scores и вам нужно получить средний балл тех, кто не сдал экзамен (с оценкой <70). Попробуем решить эту задачу с помощью цикла for:

import numpy as np

random_scores = np.random.randint(1, 100, size=10000001)

# Плохо(решение проблемы с помощью цикла for)
count_failed = 0
sum_failed = 0
for score in random_scores:
if score < 70:
sum_failed += score
count_failed += 1

print(sum_failed/count_failed)

Теперь выполним эту же задачу с помощью Numpy:

# Хорошо (решение проблемы путем использования операций с векторами)
mean_failed = (random_scores[random_scores < 70]).mean()
print(mean_failed)

Запуск обоих вариантов убеждает в том, что Numpy работает быстрее. Почему? Потому что Numpy векторизует операции.

4. Открытые файлы

Всем известна хорошая практика: каждый файл, открытый с помощью Python, должен быть закрыт.

Именно для этого при работе с файлами используются методы open, write/read и close. Если же методы write/read выбрасывают исключение, файл не будет закрыт.

Избежать проблемы поможет оператор with. Он закроет файл, даже если возникнет исключение.

# Плохо
f = open('dataset.txt', 'w')
f.write('new_data')
f.close()

# Хорошо
with open('dataset.txt', 'w') as f:
f.write('new_data')

5. Несоблюдение PEP8

PEP8  —  это документ, который должен прочитать каждый программист, изучающий Python. В нем содержатся рекомендации и лучшие практики по написанию кода на этом языке (некоторые рекомендации в данной статье взяты из PEP8).

Это руководство может показаться пугающим новичкам в Python. К счастью, некоторые правила PEP8 включены в IDE (благодаря чему я узнал о правиле чистого except).

Допустим, вы используете Pycharm. Если напишете код, который не соответствует правилам PEP8, увидите волнистые подчеркивания, как на изображении ниже.

Изображение предоставлено автором

Если наведете курсор на подчеркивания, получите инструкции по исправлению ошибок.

В моем случае нужно было только добавить пробелы после , и :.

# Хорошо
my_list = [1, 2, 3, 4, 5]
my_dict = {'key1': 'value1', 'key2': 'value2'}

my_name = "Frank"

Кроме того, я изменил имя переменной x на my_name. Pycharm не предлагал этого, но PEP8 рекомендует использовать имена переменных, которые легко понять.

6. Неправильное использование методов .keys и .values при работе со словарями

Думаю, большинство из вас знает, какие задачи решают методы .keys и .values при работе со словарями.

Если не знаете, рассмотрите следующий пример:

dict_countries = {'USA': 329.5, 'UK': 67.2, 'Canada': 38}
>>>dict_countries.keys()
dict_keys(['USA', 'UK', 'Canada'])
>>>dict_countries.values()
dict_values([329.5, 67.2, 38])

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

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

# Не использовано .keys() должным образом
# Bad
for key in dict_countries.keys():
print(key)

# Хорошо
for key in dict_countries:
print(key)

Кроме того, можно придумать обходные пути для получения значений словаря, но проще всего получить их поможет метод .items().

# Не использовано .items()
# Плохо
for key in dict_countries:
print(dict_countries[key])

# Хорошо
for key, value in dict_countries.items():
print(key)
print(value)

7. Неиспользование списковых включений (или постоянное их использование)

Списковое включение предлагает более короткий синтаксис, когда нужно создать новую последовательность (список, словарь и т. д.) на основе уже определенной последовательности.

Допустим, вам нужно перевести в нижний регистр все элементы в списке countries (страны).

Хотя это можно сделать с помощью цикла for, использование спискового включения упростит задачу.

# Плохо
countries = ['USA', 'UK', 'Canada']

lower_case = []
for country in countries:
lower_case.append(country.lower())

# Хорошо(но не злоупотребляйте этим!)
lower_case = [country.lower() for country in countries]

Списковые включения очень полезны, но не злоупотребляйте ими!

8. Использование range(len())

В числе первых функций, с которыми знакомится новичок при изучении языка Python,  —  range и len. Неудивительно, что у большинства разработчиков остается вредная привычка писать range(len()) при проходе по спискам.

Допустим, у вас есть списки countries (стран) и populations (народов). Для выполнения итерации по обоим спискам одновременно, вы, скорее всего, воспользуетесь range(len()).

# Использование range(len())
countries = ['USA', 'UK', 'Canada']
populations = [329.5, 67.2, 38]

# Плохо
for i in range(len(countries)):
country = countries[i]
population = populations[i]
print(f'{country} has a population of {population} million people')

Хотя этот код и позволяет справиться с задачей, можно упростить ее решение, используя enumerate (или, что еще лучше, функцию zip для объединения элементов из обоих списков).

# Нормально
for i, country in enumerate(countries):
population = populations[i]
print(f'{country} has a population of {population} million people')

# Лучше
for country, population in zip(countries, populations):
print(f'{country} has a population of {population} million people')

9. Форматирование с помощью оператора +

Еще один навык, который обычно усваивается на первых этапах изучения Python,  —  это объединение строк с помощью оператора +. Это полезный, но неэффективный способ. Кроме того, он может испортить внешний вид кода  —  чем больше строк нужно соединить, тем больше + придется использовать.

Вместо этого оператора, стоит применять f-строку:

# Форматирование с оператором +
# Плохо
name = input("Introduce Name: ")
print("Good Morning, " + name + "!")

# Хорошо
name = input("Introduce Name: ")
print(f'Good Morning, {name}')

Самое ценное в f-строках то, что они полезны не только для конкатенации,  —  им можно найти другие применения.

10. Использование изменяемых значений по умолчанию

Включение в функцию в качестве параметра по умолчанию изменяемого значения (например, списка) может привести к неожиданному поведению кода.

# Плохо
def my_function(i, my_list=[]):
my_list.append(i)
return my_list
>>> my_function(1)
[1]
>>> my_function(2)
[1, 2]
>>> my_function(3)
[1, 2, 3]

В приведенном выше коде каждый раз при вызове функции my_function параметр my_list будет сохранять значения из предыдущих вызовов (скорее всего, будет инициироваться пустой список при каждом вызове функции).

Чтобы избежать такого поведения, нужно установить параметр my_list равный None и включить блок if ниже:

# Хорошо
def my_function(i, my_list=None):
if my_list is None:
my_list = []
my_list.append(i)
return my_list
>>> my_function(1)
[1]
>>> my_function(2)
[2]
>>> my_function(3)
[3]

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Frank Andrade: 10 Python Mistakes That Tell You’re a Nooby

Предыдущая статьяПакетная обработка 22 ГБ данных о транзакциях с помощью Pandas
Следующая статьяПаттерн “Шаблонный метод” и его реализация в JavaScript