8 запутанных концепций Python, которые раздражают большинство разработчиков (с простыми примерами)

Python — один из самых простых для новичков языков программирования, но даже опытные разработчики сталкиваются со сложными концепциями, которые могут привести к неожиданным ошибкам и разочарованию.

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

1. Путаница is против ==

Одна из самых распространённых ошибок в Python — непонимание разницы между is и ==.

  • == проверяет, равны ли два значения
  • is проверяет, ли у двух объектов точное местоположение в памяти

Пример:

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b) # Верно, потому что значения совпадают
print(a равно b) # Неверно, потому что это разные объекты в памяти

Хотя a и b выглядят одинаково, они хранятся в разных ячейках памяти, поэтому a is b возвращает False.

💡 Совет: используйте == при сравнении значений и is при проверке, указывают ли две переменные на один и тот же объект.

2. Изменяемые аргументы по умолчанию

Классическая ошибка Python возникает при использовании изменяемых (mutable) объектов, таких как списки или словари, в качестве аргументов по умолчанию в функциях.

Пример:

add_element def(элемент, my_list=[]):
 my_list.append(элемент)
    вернуть my_list

print(add_element(1)) # [1]
print(add_element(2)) # [1, 2] (неожиданно!)

Каждый вызов add_element() изменяет дублированный список, а не создаёт новый.

Исправление: Использовать None по умолчанию

add_element def(элемент, my_list=None):
    если my_list равно None:
 my_list = []
 my_list.append(элемент)
    вернуть my_list

print(add_element(1)) # [1]
print(add_element(2)) # [2] (Верно!)

3. Чувствительность к углублению

Python использует отступы для определения блоков кода вместо фигурных скобок {}, как в других языках. Если отступы непоследовательны, вы получите сообщение об ошибке.

Пример:

Верно если:
    печать("Привет")
  print("Мир") # Ошибка отступа!

Это приведёт к ошибке IndentationError, потому что второй print() не выровнен правильно.

💡 Совет: всегда используйте пробелы или табуляцию. Большинство разработчиков Python предпочитают четыре пробела на каждый уровень отступа.

4. Глобальная блокировка интерпретатора (GIL)

Многие программисты ожидают, что многопоточность в Python будет работать так же, как в Java или C++. Однако глобальная блокировка интерпретатора (GIL) в Python позволяет одновременно выполнять код Python только одному потоку.

Это означает, что задачи, связанные с процессором, не выиграют от многопоточности в Python.

Пример:

многопроцессорный импорт

def count():
 for i in range(1000000):
 pass
if __name__ == "__main__":
 p1 = multiprocessing.Process(target=count)
 p2 = multiprocessing.Process(target=count)
 p1.start()
 p2.start()
 p1.join()
 p2.join()

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

💡 Совет: используйте многопоточность для ресурсоёмких задач вместо многопоточности.

5. Связанное назначение

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

Пример:

a = b = [] # И a, и b указывают на один и тот же список
a.добавить(1)
вывести(b) # [1] (Неожиданно!)

Поскольку a и совместно используют точное местоположение в памяти, изменение a также влияет b.

Исправление: Назначать отдельно

a = []
b = [] # Теперь это независимые списки
a.добавить (1)
напечатать(b) # []

6. Закрытие и поздняя Привязка

При использовании лямбда-функций в циклах Python применяет позднее связывание. Это означает, что переменные ищутся , когда функция выполняется, но не определена.

Пример:

funcs = [лямбда x: x + i для i в диапазоне(3)]
print([f(10) для f в функциях]) # [12, 12, 12] (неожиданно!)

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

Исправление: фиксирование текущего значения

funcs = [лямбда x, i=i: x + i для i в диапазоне(3)]
print([f(10) для f в функциях]) # [10, 11, 12] (правильно!)

7. Утиный набор текста

Python следует за утиным вводом:«Если это выглядит как утка и крякает как утка, значит, это утка».

Это означает, что Python не проверяет явные типы, но проверяет, есть ли у объекта ожидаемые методы.

Пример:

Класс утки:
    кря-кря def(self):
 print(«Кря-кря!»)

Класс собак:
    кря-кря def(self):
 print(«Я собака, но я умею крякать!»)
make_it_quack def(утка):
 утка.крякает() # Работает до тех пор, пока у объекта есть метод «крякать»
make_it_quack(Собака()) # «Я собака, но я умею крякать!»

Несмотря на то, что Dog не является Duck, он работает, потому что у него естьquack()метод.

💡 Совет: будьте внимательны при использовании «утиной типизации», так как она может привести к неожиданному поведению, если объекты не поддерживают все ожидаемые методы.

8. Распаковка с помощью *

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

Пример:

a, b, c = [1, 2, 3, 4]
выведите(a, b, c)  # ValueError! Слишком много значений для распаковки

Python ожидает ровно три значения, но получает четыре.

Исправление: Используйте * для захвата дополнительных значений

a, *b, c = [1, 2, 3, 4]
print(a, b, c)  # 1 [2, 3] 4

Здесь b фиксируются средние значения в виде списка.

Python — мощный язык, но некоторые особенности могут сбить с толку опытных разработчиков. Вот краткий обзор того, что мы рассмотрели:

✅ Используйте == для сравнения значений и is для сравнения идентификационных данных

✅ Избегайте изменяемых аргументов по умолчанию в функциях

✅ Будьте осторожны с правилами отступов в Python

✅ Используйте многопроцессорную обработку вместо многопоточности для задач, связанных с ЦП

✅ Связанное присвоение может вызвать неожиданное поведение с изменяемыми объектами

✅ Поздняя привязка в замыканиях может быть сложной; используйте i=i для захвата значений

✅ Утиный набор текста обеспечивает гибкость, но требует тщательного проектирования

✅ Распаковка с помощью * помогает обрабатывать дополнительные значения в списках

Понимание этих распространённых ошибок позволяет писать более чистый и эффективный код на Python и избегать досадных ошибок. 🚀