Углубленное ООП
Эта страница находится в процессе подготовки и не является финальной версией. Содержание будет дорабатываться и обновляться в течение текущего семестра.
Соз дание объектов: __new__ и __init__
В Python создание объекта происходит в два этапа: __new__ создаёт экземпляр, __init__ инициализирует его.
class MyClass:
def __new__(cls):
print("__new__ вызван")
instance = super().__new__(cls)
return instance
def __init__(self):
print("__init__ вызван")
obj = MyClass()
# __new__ вызван
# __init__ вызван
Если __new__ возвращает объект другого типа, __init__ не вызывается:
class A:
def __new__(cls):
return 1 # не объект A!
def __init__(self):
print("Никогда не выполнится")
A() # вернёт 1, __init__ не вызван
__slots__ — оптимизация памяти
По умолчанию атрибуты хранятся в __dict__. Использование __slots__ заменяет словарь на фиксированный массив, экономя память:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 # AttributeError — нельзя добавить новый атрибут
Используйте __slots__ для классов с большим количеством экземпляров (например, ORM-модели, точки данных). Экономия памяти может составлять 30-50%.
Дескрипторы
Дескриптор — объект, определяющий один из методов __get__, __set__ или __delete__. Дескрипторы управляют доступом к атрибутам.
class Validated:
def __init__(self, min_value, max_value):
self.min_value = min_value
self.max_value = max_value
def __set_name__(self, owner, name):
self.name = name
def __set__(self, instance, value):
if not self.min_value <= value <= self.max_value:
raise ValueError(f"{self.name}: {value} не в диапазоне")
instance.__dict__[self.name] = value
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name)
class Temperature:
celsius = Validated(-273.15, 1000)
t = Temperature()
t.celsius = 36.6 # OK
# t.celsius = -300 # ValueError
Data descriptor определяет __set__ или __delete__ и имеет приоритет над __dict__ экземпляра. Non-data descriptor определяет только __get__ — атрибут экземпляра может его перекрыть.
Properties (@property)
@property — встроенный дескриптор для создания управляемых атрибутов:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Радиус не может быть отрицательным")
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2