Обнаружение багов, edge cases, обработка исключений, null-проверки
Баги не исчезают сами. Code review — ваш шанс поймать их до продакшена.
Код работает, но выдаёт неправильный результат:
# ❌ Ошибка: неправильное условие
def is_adult(age):
return age > 18 # Должно быть >= 18
# ❌ Ошибка: перепутаны операнды
def calculate_discount(price, discount):
return price - discount * price # Должно быть price * (1 - discount)
# ❌ Ошибка: off-by-one
def get_last_element(items):
return items[len(items)] # IndexError! Должно быть len(items) - 1# ❌ Игнорирование ошибок
def load_config():
try:
return json.load(open('config.json'))
except:
return {} # Тихо игнорируем все ошибки!
# ❌ Слишком широкое исключение
def process_data(data):
try:
return transform(data)
except Exception:
return None # Ловим даже KeyboardInterrupt, SystemExit# ❌ Отсутствие проверки
def get_user_name(user):
return user.name.upper() # AttributeError если user is None
# ✅ Правильно
def get_user_name(user):
if user is None:
return None
return user.name.upper()
# ✅ Или через getattr
def get_user_name(user):
return getattr(user, 'name', None)Всегда проверяйте:
[]""{}None / null0, 0.0# ❌ Не учитывает пустой список
def calculate_average(numbers):
return sum(numbers) / len(numbers) # ZeroDivisionError для []
# ✅ Правильно
def calculate_average(numbers):
if not numbers:
return 0
return sum(numbers) / len(numbers)| Тип | Граничные значения |
|---|---|
| Числа | 0, -1, 1, min_int, max_int |
| Строки | "", 1 символ, очень длинная |
| Списки | [], [1 элемент], [очень много] |
| Даты | 0001-01-01, 9999-12-31, високосный год |
# ❌ Не учитывает отрицательные числа
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1) # Бесконечная рекурсия для n < 0
# ✅ Правильно
def factorial(n):
if n < 0:
raise ValueError("n must be non-negative")
if n == 0:
return 1
return n * factorial(n - 1)# ❌ Классическая ошибка
def add_item(item, items=[]): # [] создаётся ОДИН РАЗ!
items.append(item)
return items
add_item(1) # [1]
add_item(2) # [1, 2] — неожиданно!
# ✅ Правильно
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items# ❌ Ошибка: все функции используют одну переменную
functions = []
for i in range(5):
functions.append(lambda: i)
[f() for f in functions] # [4, 4, 4, 4, 4] — все 4!
# ✅ Правильно
functions = []
for i in range(5):
functions.append(lambda x=i: x) # Замыкаем значение
[f() for f in functions] # [0, 1, 2, 3, 4]# ❌ Неправильно
if 0.1 + 0.2 == 0.3: # False! Проблемы точности float
...
# ✅ Правильно
import math
if math.isclose(0.1 + 0.2, 0.3): # True
...# ❌ Поверхностное копирование
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0][0] = 99
print(original) # [[99, 2], [3, 4]] — изменился original!
# ✅ Глубокое копирование
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 99
print(original) # [[1, 2], [3, 4]] — original не изменилсяand / or# ❌ Ошибка: всегда True
if status == "active" or "pending": # "pending" всегда True!
...
# ✅ Правильно
if status in ("active", "pending"):
...
# ❌ Ошибка: неправильная группировка
if user.is_admin or user.is_moderator and user.is_active:
# Из-за приоритета операторов: is_admin или (is_moderator и is_active)
...
# ✅ Правильно: явная группировка
if (user.is_admin or user.is_moderator) and user.is_active:
...# ❌ Сложно читать
if not (user.is_active and user.is_verified):
return False
return True
# ✅ Проще
if user.is_active and user.is_verified:
return True
return False
# ✅ Ещё проще
return user.is_active and user.is_verified# ❌ Ошибка: модификация списка во время итерации
items = [1, 2, 3, 4, 5]
for item in items:
if item % 2 == 0:
items.remove(item) # Пропускает элементы!
# Результат: [1, 3, 5] — но 4 пропустили!
# ✅ Правильно
items = [1, 2, 3, 4, 5]
items = [item for item in items if item % 2 != 0]
# ✅ Или итерация по копии
for item in items[:]:
if item % 2 == 0:
items.remove(item)# ❌ Может выбросить KeyError
value = data["key"]
# ✅ Правильно
value = data.get("key") # None если нет ключа
value = data.get("key", default_value) # default если нет
# ✅ Или проверка
if "key" in data:
value = data["key"]При code review задавайте вопросы:
and/or)?Пройдитесь по коду мысленно с конкретными входными данными:
# Проверяем с разными input
process([]) # Пустой
process([1]) # Один элемент
process([1,2,3]) # Нормальный
process(None) # NoneПроверьте граничные значения:
# Для диапазона 1-100
process(0) # Ниже минимума
process(1) # Минимум
process(100) # Максимум
process(101) # Выше максимумаНайдите инварианты и проверьте их сохранение:
# Инвариант: сумма debits == сумма credits
def transfer(from_account, to_account, amount):
from_account.balance -= amount
to_account.balance += amount
# Инвариант сохранён?Ключевая мысль: Баги чаще всего прячутся на границах условий и в edge cases. Систематически проверяйте пустые значения, None, границы диапазонов и исключительные ситуации.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.