Использование функции property() с classmethod() в Python

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Если вам нужно определить свойство на уровне класса в Python версии 3.9 и позже, воспользуйтесь сочетанием декораторов @property и @classmethod. Ниже приведён пример его применения:

Python
Скопировать код
class MyClass:
    _my_class_value = 0

    @property
    @classmethod
    def my_value(cls):
        return cls._my_class_value

    @my_value.setter
    @classmethod
    def my_value(cls, value):
        cls._my_class_value = value

# Пример использования
MyClass.my_value = 10  # Классы также имеют свои свойства ;)
print(MyClass.my_value)  # Это свойство класса, а не экземпляра

В этом коде создаётся свойство класса my_value. Несмотря на то, что оно представлено и используется как свойство объекта, на самом деле оно принадлежит классу.

Кинга Идем в IT: пошаговый план для смены профессии

Особенности создания продвинутых свойств на уровне класса

Группировка классовых методов в метаклассе

Для улучшения читаеомости и структурированности кода рекомендуется помещать классовые методы в метакласс. Этот подход особенно полезен при работе со сложными иерархиями классов.

Python
Скопировать код
class Meta(type):
    @property
    def my_value(cls):
        return cls._my_class_value

    @my_value.setter
    def my_value(cls, value):
        cls._my_class_value = value

class MyClass(metaclass=Meta):
    _my_class_value = 0

# Пример использования
MyClass.my_value = 20  # Присваиваем значение. Оно хранится в метаклассе?
print(MyClass.my_value)  # Возвращается значение из метакласса...

Применение собственных дескрипторов для управления свойствами класса

Создавайте пользовательские дескрипторы с методами __get__, __set__ и __delete__ для гибкого управления свойствами класса.

Python
Скопировать код
class ClassPropertyDescriptor:
    def __init__(self, get=None, set=None, delete=None):
        self.getter = get
        self.setter = set
        self.deleter = delete

    def __get__(self, obj, objtype=None):
        if self.getter is None:
            raise AttributeError("Чтение недопустимо")
        return self.getter()

    def __set__(self, obj, value):
        if self.setter is None:
            raise AttributeError("Изменение недопустимо")
        self.setter(value)

    def __delete__(self, obj):
        if self.deleter is None:
            raise AttributeError("Удаление недопустимо")
        self.deleter()

Как создать свойство класса только для чтения

С помощью декоратора @classproperty можно создать свойство класса, доступное только для чтения. Это достигается путём объединения @classmethod и @property.

Python
Скопировать код
class classproperty(object):
    def __init__(self, function):
        self.function = function
    
    def __get__(self, instance, owner):
        return self.function(owner)

class MyClass:
    _my_class_value = 42

    @classproperty
    def my_value(cls):
        return cls._my_class_value

# Пример использования
print(MyClass.my_value)  # 42. Самый главный вопрос жизни, вселенной и всего такого!

Визуализация

Представьте эволюцию объекта (🌳) с атрибутами (🍃) и свойствами класса (🍎):

Markdown
Скопировать код
         🍎 (classmethod)
        /
  🍃  🍃  🍃 (property)
 / | \
🌳  🌳  🌳

property() идеально подходит для управления атрибутами (🍃), или свойствами экземпляра. Применение property() к свойствам класса (🍎) может вызвать сюрприз:

Markdown
Скопировать код
Применяем property() к 🍎 (classmethod)? 🚫
           – Это как представить, что яблоко может превратиться в апельсин!

Основное правило: property() преимущественно используется для управления атрибутами объекта, не класса.

Рекомендации и осторожности

Порядок применения дескрипторных декораторов

Порядок декораторов важен. Декоратор @classmethod должен идти перед @property для корректной работы дескриптора. Если перепутать порядок декораторов, может произойти ошибка.

Учёт версий Python при работе с метаклассами

В Python версий 3.8 до 3.10 легко создать свойства в метаклассе, но в Python 3.11 этот подход может уже не работать. Следите за актуальной документацией Python и соответствием вашего кода ей.

Повышение качества обычного свойства

Чтобы упростить типовые шаблоны, создайте подкласс property и декораторы вида @classproperty. Это улучшит читаемость кода и обеспечит возможность его переиспользования.

Python
Скопировать код
class classproperty(property):
    def __get__(self, cls, owner):
        return classmethod(self.fget).__get__(None, owner)()

class MyClass:
    _my_class_value = 'Python'

    @classproperty
    def my_value(self):
        return self._my_class_value

# Пример использования
print(MyClass.my_value)  # Выведет свойство класса — 'Python'

Метакласс как открытие глубин Инцепции!

Метакласс при определении свойств также инициализирует статические переменные и формирует принципы управления классом.

Дополнительная информация и удобства

Сравнение перед реализацией

Перед тем как реализовать свойства с помощью property и @classmethod, оцените альтернативные подходы исходя из простоты, читаемости и управляемости кода.

Эффективная инкапсуляция с помощью синтаксиса метакласса

Если в классе наблюдается "хаос", создавайте метаклассы вне его с помощью синтаксиса metaclass=.... Это упорядочивает код и ясно дифференцирует обязанности.

Одновременная работа с методами класса и экземпляра

classproperty позволяет создать свойства, которые работают на уровне и класса, и экземпляра. Это держит код консистентным и по-прежнему "pythonic".

Полезные материалы

  1. Built-in Functions — Python 3.13.0 documentation — Python документация по property.
  2. Using property() on classmethods – Stack Overflow — обсуждение использования property с классовыми методами на Stack Overflow.
  3. Understanding Class and Instance Variables in Python 3 | DigitalOcean — разъяснение работы с классовыми и экземплярными переменными в Python.
  4. Descriptor HowTo Guide — Python 3.13.0 documentation — инструкция по созданию дескрипторов в Python, подробности работы свойств.
  5. Python's Class Development Toolkit – YouTube — лекция Рэймонда Хеттингера о классовых декораторах и других особенностях Python.
  6. Python @property Decorator (With Examples) — учебное пособие по работе с декоратором @property в Python.