Добавить в корзинуПозвонить
Найти в Дзене

AttributeError, или Досадная ошибка Пола Бэрри…

*** В книге Пола Бэрри «Изучаем программирование на Python» * (кстати, очень хорошая книга по Python!) приведён такой учебный пример (сс. 348 - 349): class CountFromBy: # Создаётся пустой класс pass a = CountFromBy() # Создаются объекты класса b = CountFromBy() с = CountFromBy() c # 0 c.increase() # Каждый вызов метода «increase» увеличивает значение счётчика на единицу c.increase() c.increase() # 3. После трёх вызовов метода «increase» счётчик равен трём *** Но ведь c выводит не 0, а <__main__.CountFromBy object at 0x0000012372435FD0>, а после c.increase() выходит ошибка: Traceback (most recent call last): File "<pyshell#10>", line 1, in <module> c.increase() AttributeError: 'CountFromBy' object has no attribute 'increase' *** 1) Почему выводится адрес объекта В Python переменная класса (например, c) хранит ссылку на объект в памяти компьютера. Когда мы пишем просто имя переменной (c), интерпретатор вызывает у этого объекта специальный метод __repr__, чтобы получить
Оглавление

***

В книге Пола Бэрри «Изучаем программирование на Python» * (кстати, очень хорошая книга по Python!) приведён такой учебный пример (сс. 348 - 349):

class CountFromBy: # Создаётся пустой класс

pass

a = CountFromBy() # Создаются объекты класса

b = CountFromBy()

с = CountFromBy()

c # 0

c.increase() # Каждый вызов метода «increase» увеличивает значение счётчика на единицу

c.increase()

c.increase() # 3. После трёх вызовов метода «increase» счётчик равен трём

***

Но ведь c выводит не 0, а <__main__.CountFromBy object at 0x0000012372435FD0>, а после c.increase() выходит ошибка:

Traceback (most recent call last):

File "<pyshell#10>", line 1, in <module>

c.increase()

AttributeError: 'CountFromBy' object has no attribute 'increase'

***

Давайте разберёмся по порядку

1) Почему выводится адрес объекта

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

Поскольку наш класс пуст (pass), он наследует стандартное поведение от базового класса object. По умолчанию метод __repr__ возвращает строку вида
<ИмяМодуля.ИмяКласса object at Адрес_в_памяти>.

• <__main__.CountFromBy ...>: Это означает, что объект был создан в основном модуле программы (__main__).

• 0x...: Это шестнадцатеричный адрес ячейки памяти, где хранится данный конкретный экземпляр объекта. Каждый раз при создании нового объекта (как a, b или c) Python выделяет ему новое место в памяти, поэтому адреса будут разными.

Таким образом, вывод c – это не значение счётчика, а техническая информация о самом объекте.

2) Почему возникает ошибка AttributeError

Эта ошибка переводится как «Объект не имеет атрибута». Она происходит потому, что мы пытаемся вызвать метод .increase(), который ещё не определён внутри нашего класса.

***

Доработка учебного примера

Класс CountFromBy с ключевым словом pass является лишь пустой оболочкой. Он не знает ни о каком внутреннем счётчике и не умеет выполнять никаких действий, кроме тех, что унаследованы от object (например, сравнение или преобразование в строку). Чтобы код заработал так, как описано в книге, классу необходимо добавить внутреннее состояние и методы для работы с ним.

Для реализации такого поведения класс нужно доработать следующим образом:

1. Создать конструктор __init__, который будет инициализировать внутренний счётчик для каждого экземпляра отдельно.

2. Добавить метод increase, который будет изменять этот счётчик.

3. Переопределить метод __repr__ (или __str__), чтобы при простом вводе имени переменной (c) выводилось текущее значение счётчика, а не технический адрес.

Вот пример рабочего кода, который реализует задуманную логику **:

class CountFromBy:

def __init__(self):

# Инициализируем внутренний счётчик для каждого объекта

self.val = 0

def increase(self):

# Метод увеличивает значение счётчика на единицу

self.val += 1

def __repr__(self):

# Этот метод определяет, что выводить при вызове print(obj) или просто obj

return str(self.val)

# Создание объектов

a = CountFromBy()

b = CountFromBy()

c = CountFromBy()

print(c) # Выведет: 0

c.increase()

c.increase()

c.increase()

print(c) # Выведет: 3

-2

Теперь всё работает корректно: каждый вызов c.increase() изменяет только свойство объекта c, а печать самого объекта показывает актуальное значение его внутреннего счётчика.

***

Примечания

* Пол Бэрри. Изучаем программирование на Python. Алматы, 2024 [оригинальное название: Paul Barry. Head First Python. 2016 (перевод с английского Е. Мошковой)]

** Встроенный текстовый dzen-редактор не позволяет сохранять отступы в публикуемом программном коде (отсутствие этих отступов в Python приведёт к ошибке!).