Статьи

Учебные материалы по языку Python — от основ до продвинутых тем

property, classmethod, staticmethod, super

Страница 31 из 34 · 2026-10-26 · 4 мин чтения

Дескрипторы

Дескриптор — объект, у которого определён хотя бы один из методов __get__, __set__, __delete__. Когда такой объект лежит в атрибуте класса, обращение к атрибуту экземпляра вызывает эти методы. На дескрипторах построены property, classmethod, staticmethod и обычные методы.

property

property превращает методы в атрибут с контролируемым доступом:

class Temperature:    def __init__(self, celsius):        self._celsius = celsius    @property    def celsius(self):        return self._celsius    @celsius.setter    def celsius(self, value):        if value < -273.15:            raise ValueError("ниже абсолютного нуля")        self._celsius = value    @celsius.deleter    def celsius(self):        del self._celsius    @property    def fahrenheit(self):        return self._celsius * 9 / 5 + 32t = Temperature(25)print(t.celsius)print(t.fahrenheit)t.celsius = 30print(t.fahrenheit)

Свойство без сеттера доступно только для чтения:

try:    t.fahrenheit = 100except AttributeError as e:    print(e)
ЭлементОписание
fget, fset, fdel, __doc__Функции и документация свойства
getter(func), setter(func), deleter(func)Копия свойства с заменённой функцией

functools.cached_property

Вычисляет значение один раз и сохраняет его в экземпляре:

from functools import cached_propertyclass Report:    def __init__(self, data):        self.data = data    @cached_property    def total(self):        print("считаем")        return sum(self.data)r = Report([1, 2, 3])print(r.total)print(r.total)

classmethod

Метод класса получает класс первым аргументом (cls). Часто используется для альтернативных конструкторов:

class Date:    def __init__(self, year, month, day):        self.year = year        self.month = month        self.day = day    @classmethod    def from_string(cls, text):        y, m, d = map(int, text.split("-"))        return cls(y, m, d)    def __repr__(self):        return f"{type(self).__name__}({self.year}, {self.month}, {self.day})"class SpecialDate(Date):    passprint(Date.from_string("2026-10-26"))print(SpecialDate.from_string("2026-10-26"))

Благодаря cls метод возвращает объект подкласса, а не жёстко заданного класса.

staticmethod

Статический метод не получает ни self, ни cls. Это обычная функция, помещённая в пространство имён класса:

class MathTools:    @staticmethod    def clamp(value, low, high):        return max(low, min(value, high))print(MathTools.clamp(15, 0, 10))print(MathTools().clamp(-5, 0, 10))

Используйте его, когда функция логически относится к классу, но не зависит ни от состояния, ни от самого класса. Часто проще вынести такую функцию на уровень модуля.

Сравнение

МетодПервый аргументДоступ
Обычныйself — экземплярК экземпляру и классу
@classmethodcls — классК классу
@staticmethodнетНи к чему неявно

super

super() возвращает прокси-объект, делегирующий вызовы следующему классу в MRO:

class Base:    def __init__(self, name):        self.name = name    def describe(self):        return f"Base {self.name}"class Child(Base):    def __init__(self, name, extra):        super().__init__(name)        self.extra = extra    def describe(self):        return super().describe() + f" + {self.extra}"print(Child("a", "b").describe())

Множественное наследование

super() следует по MRO, поэтому в ромбовидной иерархии каждый класс вызывается ровно один раз:

class A:    def hello(self):        print("A")class B(A):    def hello(self):        print("B")        super().hello()class C(A):    def hello(self):        print("C")        super().hello()class D(B, C):    def hello(self):        print("D")        super().hello()D().hello()print([c.__name__ for c in D.__mro__])

Результат: D, B, C, A, затем MRO ['D', 'B', 'C', 'A', 'object'].

Собственные дескрипторы

class Positive:    def __set_name__(self, owner, name):        self.name = "_" + name    def __get__(self, instance, owner):        if instance is None:            return self        return getattr(instance, self.name)    def __set__(self, instance, value):        if value <= 0:            raise ValueError("значение должно быть положительным")        setattr(instance, self.name, value)class Product:    price = Positive()    quantity = Positive()    def __init__(self, price, quantity):        self.price = price        self.quantity = quantityp = Product(10, 3)print(p.price)try:    p.quantity = -1except ValueError as e:    print(e)

Другие вспомогательные встроенные классы

КлассНазначение
functools.partialФиксация части аргументов функции
functools.wrapsКопирование метаданных функции в обёртку
functools.singledispatchПерегрузка функции по типу первого аргумента
functools.total_orderingГенерация сравнений по __eq__ и одному из __lt__ и т. п.
types.SimpleNamespaceПростой объект с произвольными атрибутами
types.MappingProxyTypeНеизменяемое представление словаря
from functools import partial, total_orderingfrom types import SimpleNamespacehex_to_int = partial(int, base=16)print(hex_to_int("ff"))ns = SimpleNamespace(a=1, b=2)ns.c = 3print(ns)@total_orderingclass Version:    def __init__(self, major, minor):        self.key = (major, minor)    def __eq__(self, other):        return self.key == other.key    def __lt__(self, other):        return self.key < other.keyprint(Version(1, 2) <= Version(1, 3))print(Version(2, 0) > Version(1, 9))

Задания

  1. Напишите класс Circle со свойствами radius, diameter и area, чтобы изменение диаметра меняло радиус.
  2. Реализуйте классовые конструкторы from_json и from_dict для класса настроек.
  3. Создайте дескриптор, проверяющий тип присваиваемого значения.