AttributeError: object has no attribute в Python — как проверить тип и имя

Python Автор: Среда и версия: CPython 3.14.5
содержание

AttributeError: object has no attribute возникает, когда доступ к атрибуту или его присваивание не удались. В сообщении обычно есть фактический тип объекта и имя атрибута: 'list' object has no attribute 'items'. Но это не всегда означает, что такого имени нигде нет: property, дескриптор, __getattr__ или __getattribute__ могут сами выбросить AttributeError во время вычисления значения.

Поэтому сначала прочитайте traceback целиком и найдите выражение с упавшей точкой. Затем выделите объект слева от этой точки и проверьте его runtime-тип. Примеры ниже проверены на CPython 3.14.5.

Python / 01

На каком объекте пропал атрибут

На каком объекте пропал атрибут01 Раздели цепочку account.profile.email Сначала получи account.profile отдельно. 02 Проверь основу profile is None? None не имеет email. Найди источник отсутствующего профиля. 03 Если объект есть type(profile) Проверь экземпляр, класс и property: где должен жить email.01Раздели цепочкуaccount.profile.emailСначала получи account.profile отдельно.02Проверь основуprofile is None?None не имеет email. Найди источник отсутствующего профиля.03Если объект естьtype(profile)Проверь экземпляр, класс и property: где должен жить email.
В цепочке с несколькими точками проверяют не всю строку разом, а конкретный объект слева от атрибута: здесь это account.profile перед .email.

Сначала найдите точный объект и атрибут

В цепочке account.profile.email.lower() три обращения к атрибутам. Сообщение и последний кадр traceback показывают, на каком из них остановилось выполнение:

Traceback (most recent call last):
  File "main.py", line 8, in <module>
    print(account.profile.email.lower())
          ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'email'

Здесь метод lower() ещё не вызывался. Атрибут profile найден, но его значение равно None; уже у этого значения код запросил email. Разделите цепочку и распечатайте промежуточный объект:

class Account:
    profile = None

account = Account()
profile = account.profile
print(repr(profile))
print(type(profile).__name__)
None
NoneType

Проверяйте объект в том же запуске и как можно ближе к месту ошибки. Тип из аннотации, документации или окна отладчика не заменяет type(value) во время падения: в переменной уже могло оказаться другое значение.

Почему появляется NoneType object has no attribute

NoneType — тип единственного объекта None. Обычно функция ничего не нашла, одна из веток завершилась без return или метод изменил объект на месте и вернул None.

def find_user(users, user_id):
    return users.get(user_id)

user = find_user({7: {"email": "dev@example.com"}}, 9)
print(user is None)
print(type(user).__name__)
True
NoneType

Исправление зависит от контракта функции. Если отсутствие пользователя нормально, обработайте его до доступа к данным:

user = find_user(users, user_id)
if user is None:
    return "пользователь не найден"

send_email(user["email"])

Если None невозможен по правилам программы, не маскируйте его через getattr(user, "email", "") и не пытайтесь присвоить user.email вслепую. Найдите ветку, которая вернула None, и исправьте её. Для словаря нужен ключ user["email"], а не атрибут; разница между доступом к ключам и методами разобрана в статье про dict.

Отдельно проверьте методы, которые меняют объект на месте. Например, list.sort() возвращает None:

names = ["Вера", "Аня"]
result = names.sort()
print(names)
print(result)
['Аня', 'Вера']
None

После result = names.sort() вызов result.append(...) упадёт. Продолжать работу нужно с names, а если требуется новый список — использовать sorted(names).

Проверьте опечатку и регистр

Имена атрибутов чувствительны к регистру. user.full_name, user.Full_Name и user.fullName — три разных имени. Сверьте написание с местом объявления или документацией библиотеки, включая подчёркивания.

class User:
    def __init__(self, full_name):
        self.full_name = full_name

user = User("Анна Смирнова")
print(user.fullName)
AttributeError: 'User' object has no attribute 'fullName'. Did you mean: 'full_name'?

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

Убедитесь, что метод вызывается у того типа

Частая причина — имя метода верное, но объект не того типа. У словаря есть items(), у списка нет:

records = [{"id": 1}, {"id": 2}]
print(type(records).__name__)
print(records[0].items())
list
dict_items([('id', 1)])

Если вызвать records.items(), получится 'list' object has no attribute 'items'. Исправление зависит от данных: перебрать список словарей, выбрать один элемент либо изменить функцию, которая вернула список вместо ожидаемого словаря. Добавлять items самому списку не нужно.

То же относится к методам строк, списков и словарей: append() принадлежит списку, lower() — строке, get() — словарю. Проверяйте не только наличие похожего метода, но и форму данных, которую должен обрабатывать этот участок программы.

Ветка init не создала атрибут экземпляра

Атрибут экземпляра может появиться лишь при выполнении определённой ветки. Тогда объекты одного класса будут устроены по-разному:

class Report:
    def __init__(self, is_draft):
        if is_draft:
            self.title = "Черновик"

published = Report(False)
print(published.title)
AttributeError: 'Report' object has no attribute 'title'

Создайте обязательный атрибут до ветвления, а внутри условия меняйте его значение:

class Report:
    def __init__(self, is_draft):
        self.title = None
        if is_draft:
            self.title = "Черновик"

published = Report(False)
print(published.title)
None

Так у каждого Report одинаковый набор обязательных полей. Если None недопустим, конструктор должен получить заголовок или выбросить подходящее исключение сразу, а не создавать частично готовый объект.

Атрибут класса и атрибут экземпляра — не одно и то же

Имя в теле класса принадлежит классу. Присваивание через self обычно создаёт атрибут конкретного экземпляра:

class Session:
    timeout = 30

    def __init__(self, token):
        self.token = token

session = Session("abc")
print(Session.timeout, session.timeout)
print(session.token)
30 30
abc

Session.token завершится с AttributeError: token появляется только после создания экземпляра. Обратный доступ обычно работает: если имени нет в экземпляре, Python продолжает поиск в классе и его базовых классах. Поэтому session.timeout находит классовое значение 30.

Не переносите token = None в тело класса только ради исчезновения ошибки. Это меняет модель данных и может особенно навредить с изменяемыми значениями: один классовый список будет общим для всех экземпляров. Для декларативных моделей используйте предусмотренный ими способ объявления полей; базовый вариант с генерируемым __init__ показан в статье про dataclass.

Property и дескриптор могут сами выбросить AttributeError

Доступ profile.display_name может найти display_name в классе и вызвать его property, а ошибка возникнет уже внутри свойства:

class Profile:
    @property
    def display_name(self):
        return self.raw_name.strip()

profile = Profile()
print(profile.display_name)
Traceback (most recent call last):
  File "main.py", line 7, in <module>
    print(profile.display_name)
          ^^^^^^^^^^^^^^^^^^^^
  File "main.py", line 4, in display_name
    return self.raw_name.strip()
           ^^^^^^^^^^^^^
AttributeError: 'Profile' object has no attribute 'raw_name'

Внешнее имя display_name существует. Не создана его зависимость raw_name. Это видно только по полному traceback: кадр свойства находится выше последней строки.

Дескрипторы, __getattribute__ и __getattr__ тоже выполняют пользовательский код во время доступа. Они могут вернуть вычисленное значение, создать атрибут динамически или поднять AttributeError. Поэтому формула «AttributeError всегда означает, что имени нет» неверна.

Проверьте, какой модуль импортирован

Локальный файл может затенить модуль или сторонний пакет с тем же именем. Тогда импорт проходит, но у загруженного модуля нет ожидаемого атрибута:

import json

print(json.__name__)
print(getattr(json, "__file__", None))
print(json.dumps)

Если путь указывает на /project/json.py, а вы ожидали модуль стандартной библиотеки, переименуйте локальный файл и перезапустите процесс. У некоторых встроенных или динамически созданных модулей __file__ может отсутствовать, поэтому диагностический getattr(..., None) здесь уместен.

Похожий случай, когда модуль не находится вообще, даёт другую ошибку. Проверка интерпретатора, sys.path и затенения разобрана в руководстве по ModuleNotFoundError.

Как использовать getattr, hasattr, dir и inspect.getattr_static

getattr(obj, name) нужен, когда имя атрибута хранится в строке. Третий аргумент задаёт запасное значение:

missing = object()
value = getattr("python", "casefold", missing)
print(value is missing)
print(callable(value))
False
True

Для отличия отсутствующего атрибута от настоящего None используйте уникальный объект-сентинел, а не None. Но getattr выполняет обычный динамический поиск: он может вызвать property, дескриптор, __getattribute__ или __getattr__. Вариант с запасным значением способен скрыть AttributeError, возникший внутри такого кода.

hasattr(obj, name) делает тот же доступ и возвращает False, если он завершился именно AttributeError. Другие исключения функция не подавляет. Проверка может выполнить код свойства, изменить состояние, записать лог или сделать запрос, поэтому hasattr не гарантирует безопасную пассивную проверку.

dir(obj) полезен для быстрого поиска опечатки, но это подсказка, а не полный контракт объекта. Класс может переопределить __dir__; динамические атрибуты из __getattr__ иногда не попадают в список, а показанное имя всё равно может упасть при чтении.

Для отладки без вызова динамического доступа есть inspect.getattr_static():

import inspect

class Invoice:
    @property
    def total(self):
        raise AttributeError("сумма ещё не рассчитана")

descriptor = inspect.getattr_static(Invoice(), "total")
print(type(descriptor).__name__)
property

getattr_static нашёл сам объект property, не выполняя его. У этого инструмента есть обратные ограничения: он может пропустить динамически создаваемый атрибут, вернуть дескриптор вместо значения и не увидеть поля экземпляра, если __dict__ затенён другим членом класса. Используйте его для диагностики устройства объекта, а не как замену обычному доступу в рабочей логике.

Не ловите AttributeError вокруг большой цепочки

Такой код маскирует слишком много разных дефектов:

try:
    return account.profile.avatar.url
except AttributeError:
    return None

Отсутствовать может profile, avatar или url; кроме того, AttributeError способен возникнуть внутри любого свойства. Обработчик сотрёт traceback и превратит баг в пустое значение.

Разделите цепочку, проверьте ожидаемый необязательный результат явно и оставьте неожиданные ошибки видимыми:

profile = account.profile
if profile is None:
    return None

avatar = profile.avatar
if avatar is None:
    return None

return avatar.url

Перехватывать AttributeError стоит только на узкой границе API, где отсутствие конкретного атрибута документировано как нормальный сценарий. Не используйте голый except или except Exception ради продолжения работы. Правила выбора узкого обработчика собраны в статье про try/except.

Таблица диагностики AttributeError

Сообщение или симптомЧто проверитьЧто исправить
'NoneType' object has no attribute 'email'Значение объекта слева от .email, ветки return, методы с изменением на местеОбработать допустимый None или исправить источник неожиданного None
'list' object has no attribute 'items'type(value), форму коллекции, элемент спискаПеребирать список, выбрать словарь или исправить возвращаемый тип
Имя отличается регистром или стилемОбъявление атрибута, документацию, dir(obj) как подсказкуИсправить точное написание, не создавать второе похожее имя
Ошибка только у части экземпляровВсе ветки __init__, фабрики и альтернативные конструкторыСоздавать обязательное поле на каждом пути инициализации
Class.field падает, а instance.field работаетГде присваивается имя: в теле класса или через selfОбращаться через экземпляр либо объявить настоящее классовое значение
Имя видно в классе, но чтение падаетПолный traceback внутри property, __get__, __getattr__Исправить код или зависимость вычисляемого атрибута
У модуля нет ожидаемого методаmodule.__name__, getattr(module, "__file__", None)Убрать затеняющий файл или импортировать нужный модуль
hasattr неожиданно возвращает FalseНе выбрасывает ли свойство внутренний AttributeErrorПроверить traceback прямого доступа и устройство через getattr_static

Короткий порядок действий

  1. Прочитайте последнюю строку и все кадры traceback, включая код внутри property.
  2. В упавшем выражении назовите точный объект слева от точки и точное имя справа.
  3. Сохраните промежуточный объект и проверьте repr(value), type(value) и value is None.
  4. Сверьте регистр и убедитесь, что метод принадлежит фактическому типу.
  5. Для своего класса проверьте все ветки __init__, затем различите поля экземпляра и класса.
  6. Для модуля распечатайте __file__; для динамического доступа учитывайте выполнение дескрипторов.
  7. Исправьте источник неверного объекта или неполной инициализации, не скрывая ошибку широким try/except.

Такой порядок отделяет обычную опечатку от ошибки данных и от сбоя внутри вычисляемого атрибута. Главное доказательство — фактический объект в точке падения, а не предположение о том, что должно было лежать в переменной.

Источники