Курс Python → None в Python: использование и особенности

Короче, в Python есть такой специальный парень, который называется None. Это не просто пустота, это конкретный объект, который символизирует отсутствие чего-либо. Типа, когда у тебя нет данных, или результат неизвестен, или вот пока не знаю, что будет. Это как чистый лист бумаги, на котором еще ничего не написано, но сам лист уже есть. В программировании такое часто бывает, и None тут как тут, чтобы помочь разобраться.

Его основная фишка — показывать, что чего-то нет. Например, если функция ничего не возвращает явно, то она автоматически отдаст None. Это удобно, чтобы понять, что она отработала, но не выдала никакого результата. Еще None используют как значение по умолчанию для параметров функций. Это типа «если ты ничего не передал, то вот тебе None, а дальше сам решай». Это круто, потому что позволяет делать функции более гибкими, особенно когда речь идет о всяких там списках, которые могут меняться.

Сравнивать None с null из других языков? Ну, по сути, это одно и то же — отсутствие значения. Но в Python это всегда None, единый такой объект. Он относится к типу NoneType. Так что, если вы раньше работали с null, то в Python просто заменяете его на None и всё.

Как Python работает с None: Примеры использования

Давайте посмотрим, как этот None вообще себя ведет в реальном коде. Это реально полезно, чтобы не попасть впросак.

Во-первых, когда функция не доходит до return или в ней вообще нет return, она автоматически возвращает None. Вот пример:


def say_hello(name):
    print(f"Привет, {name}!")

result = say_hello("Мир")
print(result) # Выведет: None

Видите? Функция отработала, но ничего не вернула, поэтому мы получили None. Это прямо такое указание, что всё, конец, результат отсутствует.

Еще одна классная штука — использовать None как значение по умолчанию для аргументов. Особенно это важно, когда мы имеем дело с мутабельными типами, типа списков. Вот почему нельзя просто так написать def func(arg=[]):. Если вы будете менять этот список внутри функции, он будет меняться для всех вызовов, где аргумент не был передан. А вот с None всё гораздо безопаснее:


def add_item(item, data=None):
    if data is None:
        data = []
    data.append(item)
    return data

my_list = add_item(1)
print(my_list) # Выведет: [1]
my_list_2 = add_item(2, my_list)
print(my_list_2) # Выведет: [1, 2]

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

None также отлично подходит для инициализации переменных. Бывает, вы знаете, что переменная будет, но ее значение появится чуть позже, может, из базы данных или от пользователя. Можно просто присвоить ей None:


user_data = None
# ... позже, когда получаем данные ...
user_data = fetch_user_from_db(user_id)

if user_data is not None:
    print(f"Пользователь найден: {user_data['name']}")
else:
    print("Пользователь не найден.")

Ну и, конечно, при работе с базами данных, если поле пустое, оно может прийти как None. Это стандартная практика, чтобы обозначить, что данных просто нет, а не что там ошибка или какая-то другая фигня.

Сравнение с None: `is` против `==`

Вот тут начинается самое интересное, и многие на этом спотыкаются. Сравнение с None — это прямо такая тема. Помните, я говорил, что None — это синглтон? Это значит, что существует только один-единственный экземпляр None во всей вселенной Python. Всегда. Ну, типа, как один единственный босс, который отвечает за всё отсутствие.

И вот из-за этой его уникальности, проверять, является ли что-то None, лучше всего с помощью оператора is. Почему? Потому что is проверяет не само значение, а идентичность объектов. Он спрашивает: «Ты — это тот самый, единственный None?» А вот оператор == проверяет равенство значений. Он спрашивает: «Твое значение такое же, как у None

В большинстве случаев, когда вы проверяете что-то на None, вы хотите именно узнать, является ли переменная этим конкретным объектом None. Поэтому is None — это самый правильный, самый питонячий (pythonic) способ.

Пример:


my_var = None

# Правильно и рекомендуется
if my_var is None:
    print("Переменная равна None")

# Тоже сработает, но менее предпочтительно
if my_var == None:
    print("Переменная равна None")

# А вот так проверяем, что НЕ None
if my_var is not None:
    print("Переменная не равна None")

Почему is лучше? Иногда, когда вы создаете свои классы, вы можете переопределить оператор `==`. И тогда ваш объект, который на самом деле не None, может начать вести себя так, будто он равен None. А is так не сделает, он будет честен и скажет, что это разные объекты. Так что, короче, запомните: для None — только is!

Особенности и ловушки при работе с None

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

Первое, что вам стоит запомнить: с None нельзя делать математические операции. Если попробуете сложить None с числом, получите TypeError. Это типа как пытаться посчитать, сколько пальцев на руке у призрака — бессмысленно. Нужно сначала убедиться, что там есть что-то реально существующее.

Еще одна фишка — «ложность» (truthiness) значений в Python. None считается «falsey», то есть в условных выражениях он ведет себя как False. Но это не значит, что он равен False! Это важно. Например, if not my_var: сработает, если my_var равен None, но if my_var == False: — нет. Разница может показаться мелкой, но в логике кода она иногда играет огромную роль.

Часто None приводит к AttributeError. Это происходит, когда вы пытаетесь вызвать метод у переменной, которая оказалась None. Например:


user = get_user_from_session() # Может вернуть None, если пользователя нет

# Ошибка! user - это None, у него нет метода .name
# print(user.name)

Чтобы избежать этого, всегда проверяйте, не равен ли объект None, прежде чем пытаться работать с его атрибутами или методами. Вот тут if user is not None: — ваш лучший друг.

И еще одно. Если вы попытаетесь использовать переменную, которой вообще никогда не присваивали никакого значения, вы получите NameError. Она не будет вести себя как None. None — это конкретное значение, которое было присвоено. А NameError — это когда Python даже не знает, о чем вы говорите. Разные вещи.

None в контексте типизации Python

Ну и под конец, давайте заглянем в будущее, где у нас есть типизация в Python. Как там None себя чувствует?

Когда мы используем подсказки типов (type hints) в Python, например, с модулем typing, мы можем явно указать, что функция может вернуть None или что параметр может быть None. Для этого есть специальный тип — Optional. Это типа «или вот это, или None». Звучит немного странно, но на деле очень удобно.

Например, если функция может вернуть строку или ничего, мы можем написать так:


from typing import Optional

def get_user_name(user_id: int) -> Optional[str]:
    # ... логика получения имени ...
    if user_found:
        return "Иван"
    else:
        return None

user_name = get_user_name(123)

if user_name: # Тут Python сам поймет, что user_name может быть None
    print(f"Привет, {user_name}!")
else:
    print("Пользователь не найден.")

Использование Optional делает код гораздо понятнее. Статические анализаторы кода (типа mypy) тоже это ценят. Они смогут вам подсказать, где вы забыли обработать случай с None, прежде чем ваш код вообще запустится. Это такая дополнительная страховка, чтобы меньше ошибок было в продакшене.

Твои коллеги будут рады, поделись в

Автор урока

Дмитрий Комаровский
Дмитрий Комаровский

Автоматизация процессов
в КраснодарБанки.ру

Другие уроки курса "Python"

  1. Операторы присваивания в Python
  2. Объединение списков в Python
  3. Перевод двоичного кода в целое число
  4. Использование модуля __future__
  5. Ноутбуки AMD Ryzen ™ 4000: мощность и эффективность
  6. Создание вложенного генератора
  7. Расширение информации об ошибке в Python
  8. Дизассемблирование Python кода
  9. Многопоточность и асинхронное программирование в Python
  10. Работа с модулем glob в Python
  11. Метод enumerate() в Python
  12. Возврат нескольких значений
  13. Построение графиков в Matplotlib
  14. Явный импорт переменных
  15. Тестирование с unittest
  16. Лямбда-функции в Python
  17. Функция enumerate() в Python
  18. Список переменных с %who
  19. Работа со строками в Python
  20. Печать списка с помощью метода join
  21. Объединение итераторов
  22. Итерация по итерируемым объектам
  23. Регистрация на хакатоне
  24. Создание Radio кнопок в tkinter
  25. Приближение чисел в Python
  26. Работа с collections.Counter
  27. Декораторы в Python
  28. Работа с множествами в Python
  29. Работа с NumPy.linalg
  30. Установка Git и AWS CLI
  31. Поиск индексов подстроки
  32. Создание словарей с defaultdict
  33. Вывод переменной и строки в Python
  34. split() — разделение строки
  35. Присоединение элементов коллекции
  36. Flask: создание веб-приложений
  37. Избегание изменяемых аргументов
  38. Перезагрузка оператора в Python
  39. Обработка ошибок в Python
  40. Конвертация изображений в PDF
  41. Отладка в командной строке
  42. Частичное совпадение пользовательского ввода в Python 3.10
  43. Обработка данных в Python
  44. OrderedDict — упорядоченный словарь

Marketello читают маркетологи из крутых компаний