Использование функции property() с classmethod() в Python
Быстрый ответ
Если вам нужно определить свойство на уровне класса в Python версии 3.9 и позже, воспользуйтесь сочетанием декораторов @property
и @classmethod
. Ниже приведён пример его применения:
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
. Несмотря на то, что оно представлено и используется как свойство объекта, на самом деле оно принадлежит классу.
Особенности создания продвинутых свойств на уровне класса
Группировка классовых методов в метаклассе
Для улучшения читаеомости и структурированности кода рекомендуется помещать классовые методы в метакласс. Этот подход особенно полезен при работе со сложными иерархиями классов.
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__
для гибкого управления свойствами класса.
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
.
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. Самый главный вопрос жизни, вселенной и всего такого!
Визуализация
Представьте эволюцию объекта (🌳) с атрибутами (🍃) и свойствами класса (🍎):
🍎 (classmethod)
/
🍃 🍃 🍃 (property)
/ | \
🌳 🌳 🌳
property()
идеально подходит для управления атрибутами (🍃), или свойствами экземпляра. Применение property()
к свойствам класса (🍎) может вызвать сюрприз:
Применяем property() к 🍎 (classmethod)? 🚫
– Это как представить, что яблоко может превратиться в апельсин!
Основное правило: property()
преимущественно используется для управления атрибутами объекта, не класса.
Рекомендации и осторожности
Порядок применения дескрипторных декораторов
Порядок декораторов важен. Декоратор @classmethod
должен идти перед @property
для корректной работы дескриптора. Если перепутать порядок декораторов, может произойти ошибка.
Учёт версий Python при работе с метаклассами
В Python версий 3.8 до 3.10 легко создать свойства в метаклассе, но в Python 3.11 этот подход может уже не работать. Следите за актуальной документацией Python и соответствием вашего кода ей.
Повышение качества обычного свойства
Чтобы упростить типовые шаблоны, создайте подкласс property
и декораторы вида @classproperty
. Это улучшит читаемость кода и обеспечит возможность его переиспользования.
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".
Полезные материалы
- Built-in Functions — Python 3.13.0 documentation — Python документация по
property
. - Using property() on classmethods – Stack Overflow — обсуждение использования
property
с классовыми методами на Stack Overflow. - Understanding Class and Instance Variables in Python 3 | DigitalOcean — разъяснение работы с классовыми и экземплярными переменными в Python.
- Descriptor HowTo Guide — Python 3.13.0 documentation — инструкция по созданию дескрипторов в Python, подробности работы свойств.
- Python's Class Development Toolkit – YouTube — лекция Рэймонда Хеттингера о классовых декораторах и других особенностях Python.
- Python @property Decorator (With Examples) — учебное пособие по работе с декоратором
@property
в Python.