🐛 Типичные ошибки
⚡ Кратко
Сверяйте сообщение об ошибке с причиной и исправлением. Воспроизведите хотя бы одну ошибку самостоятельно и затем устраните её.
Ошибка 1. Забыли self при обращении к приватному атрибуту
class Book:
def __init__(self, title: str) -> None:
self.__title = title
def show_title(self) -> None:
print(__title) # NameError: name '_Book__title' is not defined
Почему: внутри метода __title манглируется в _Book__title, но без self Python ищет глобальное имя.
Исправление:
def show_title(self) -> None:
print(self.__title)
Ошибка 2. Обращение к приватному атрибуту извне
book = Book("1984")
print(book.__title) # AttributeError
Почему: имя манглировано в _Book__title, поэтому прямое обращение не работает.
Исправление: используйте публичный метод или свойство, предоставленное классом.
Ошибка 3. Путаница в имени свойства
class Temperature:
def __init__(self, value: float) -> None:
self.__celsius = value
@property
def __celsius(self) -> float: # манглированное имя, не то, что ожидалось
return self.__celsius
t = Temperature(25)
print(t.celsius) # AttributeError
Почему: свойство должно называться celsius, а не __celsius.
Исправление:
@property
def celsius(self) -> float:
return self.__celsius
Ошибка 4. Отсутствие сеттера у @property
class Circle:
def __init__(self, radius: float) -> None:
self.__radius = radius
@property
def radius(self) -> float:
return self.__radius
c = Circle(5)
c.radius = 10 # AttributeError: can't set attribute
Почему: если нужно изменять свойство, определите сеттер через @radius.setter.
Исправление: добавьте сеттер или сделайте свойство read-only осознанно.
Ошибка 5. Попытка переопределить приватный метод в наследнике
class Base:
def __init__(self) -> None:
self.__setup()
def __setup(self) -> None:
print("base setup")
class Child(Base):
def __setup(self) -> None: # не переопределяет Base.__setup
print("child setup")
Child() # base setup
Почему: приватный метод манглируется в _Base__setup и _Child__setup, поэтому они не связаны.
Исправление: используйте защищённый метод _setup, если нужно переопределение.
Ошибка 6. В __init__ присвоили приватное поле напрямую — сеттер не сработал
class Person:
def __init__(self, age: int) -> None:
self.__age = age # проверка обойдена
@property
def age(self) -> int:
return self.__age
@age.setter
def age(self, value: int) -> None:
if value < 0:
raise ValueError("Возраст не может быть отрицательным")
self.__age = value
p = Person(-10)
print(p.age) # -10, хотя сеттер запрещает отрицательные значения
Почему: self.__age = age пишет прямо в приватное поле, а сеттер вызывается только при обращении к свойству age.
Исправление: в __init__ писать self.age = age — тогда проверка выполнится при создании объекта.
Ошибка 7. Бесконечная рекурсия в дескрипторе
class Age:
def __set__(self, instance, value: int) -> None:
instance.age = value # снова вызывает Age.__set__ → рекурсия
class Person:
age = Age()
def __init__(self, age: int) -> None:
self.age = age
Person(25) # RecursionError: maximum recursion depth exceeded
Почему: instance.age — это тот самый атрибут, которым управляет дескриптор, поэтому присваивание снова попадает в __set__.
Исправление: хранить значение под другим именем: instance._age = value или setattr(instance, self.private_name, value) при использовании __set_name__.
Ошибка 8. Имя дескриптора не совпадает с именем атрибута
class PositiveNumber:
def __set_name__(self, owner, name: str) -> None:
self.private_name = "_" + name
def __set__(self, instance, value: float) -> None:
if value < 0:
raise ValueError("Значение не может быть отрицательным")
setattr(instance, self.private_name, value)
class Person:
balance = PositiveNumber()
def __init__(self, balance: float) -> None:
self.money = balance # ❌ атрибут называется иначе
p = Person(-100)
print(p.money) # -100 — дескриптор не участвовал
Почему: дескриптор срабатывает только при обращении к атрибуту с тем же именем, под которым он объявлен в классе (balance).
Исправление: присваивать self.balance = balance.