Использование декоратора property

Введение в декораторы

Декораторы в Python — это функции, которые позволяют модифицировать поведение других функций или методов. Они используются для добавления функциональности к существующим методам или атрибутам без изменения их кода. Один из наиболее распространенных случаев использования декораторов — это создание свойств (properties) с помощью декоратора property.

Декоратор property позволяет управлять доступом к атрибутам объекта, создавая геттеры и сеттеры, которые обеспечивают контроль над их значениями. Это особенно полезно для инкапсуляции данных и управления доступом к ним.

 

Создание класса с использованием property

Давайте рассмотрим, как работает декоратор property на примере простого класса Circle, который представляет круг. Этот класс будет иметь радиус и позволит вычислять площадь круга.

class Circle:
    def __init__(self, radius):
        self._radius = radius  # Приватный атрибут для хранения радиуса

    @property
    def radius(self):
        """Геттер для атрибута radius."""
        return self._radius

    @radius.setter
    def radius(self, value):
        """Сеттер для атрибута radius."""
        if value <= 0:
            raise ValueError("Radius must be positive!")
        self._radius = value

    @property
    def area(self):
        """Свойство для вычисления площади круга."""
        return 3.14159 * (self._radius ** 2)

Объяснение примера 1

  • Конструктор __init__: Мы инициализируем атрибут _radius, который хранит значение радиуса. Он приватный (начинается с _), что сигнализирует о том, что он не предназначен для прямого доступа извне класса.
  • Геттер radius: Декоратор @property позволяет нам определить метод, который будет вызываться при обращении к свойству radius. Этот метод возвращает текущее значение _radius.
  • Сеттер radius: Декоратор @radius.setter определяет метод, который вызывается при установке нового значения для radius. Здесь мы проверяем, является ли новое значение положительным, и выбрасываем исключение ValueError, если нет.
  • Свойство area: Мы определяем свойство area, которое вычисляет площадь круга на основе радиуса. Оно не имеет сеттера, так как площадь зависит только от радиуса.


Создание объекта и использование класса Circle

Теперь создадим объект класса Circle и протестируем его методы.

circle = Circle(5)
print("Radius:", circle.radius)  # Получаем радиус
print("Area:", circle.area)       # Получаем площадь
circle.radius = 10                 # Устанавливаем новый радиус
print("New Radius:", circle.radius)  # Получаем новый радиус
print("New Area:", circle.area)      # Получаем новую площадь

Вывод:

Radius: 5
Area: 78.53975
New Radius: 10
New Area: 314.159

Объяснение вывода

  • Вначале мы создаем круг с радиусом 5 и выводим его радиус и площадь. Площадь вычисляется по формуле π * r².
  • Затем мы изменяем радиус на 10 и снова выводим радиус и площадь. Как видно, площадь увеличилась, поскольку она зависит от квадрата радиуса.


Обработка ошибок с использованием property

Теперь давайте добавим логику обработки ошибок при установке значений с помощью декоратора property. Мы уже видели, как выбрасывается ValueError, если радиус не положительный. Давайте добавим в наш пример дополнительную логику.

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if not isinstance(value, (int, float)):
            raise TypeError("Radius must be a number!")
        if value <= 0:
            raise ValueError("Radius must be positive!")
        self._radius = value

    @property
    def area(self):
        return 3.14159 * (self._radius ** 2)


Создание объекта и тестирование обработки ошибок

Теперь протестируем новую логику обработки ошибок.

try:
    circle = Circle(5)
    print("Radius:", circle.radius)  # Получаем радиус
    circle.radius = -3                 # Пытаемся установить отрицательный радиус
except ValueError as e:
    print(e)                          # Выводим сообщение об ошибке

try:
    circle.radius = "ten"             # Пытаемся установить строку вместо числа
except TypeError as e:
    print(e)                          # Выводим сообщение об ошибке

Вывод:

Radius: 5
Radius must be positive!
Radius must be a number!

Объяснение вывода

  • При попытке установить отрицательный радиус, метод-сеттер выбрасывает ValueError, и мы обрабатываем это исключение, выводя соответствующее сообщение.
  • При попытке установить строку вместо числа мы получаем TypeError, и также обрабатываем это исключение.


Сложные свойства с логикой

Давайте создадим более сложный пример, где свойство будет иметь логику, связанную с несколькими атрибутами. Мы создадим класс Rectangle, который будет иметь ширину и высоту, а также свойство для вычисления периметра.

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    @property
    def width(self):
        return self._width

    @width.setter
    def width(self, value):
        if value <= 0:
            raise ValueError("Width must be positive!")
        self._width = value

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, value):
        if value <= 0:
            raise ValueError("Height must be positive!")
        self._height = value

    @property
    def perimeter(self):
        return 2 * (self._width + self._height)


Создание объекта и использование класса Rectangle

Теперь создадим объект класса Rectangle и протестируем его свойства.

rectangle = Rectangle(5, 10)
print("Width:", rectangle.width)        # Получаем ширину
print("Height:", rectangle.height)      # Получаем высоту
print("Perimeter:", rectangle.perimeter)  # Получаем периметр
rectangle.width = 7                      # Устанавливаем новую ширину
rectangle.height = 12                    # Устанавливаем новую высоту
print("New Width:", rectangle.width)     # Получаем новую ширину
print("New Height:", rectangle.height)   # Получаем новую высоту
print("New Perimeter:", rectangle.perimeter)  # Получаем новый периметр

Вывод:

Width: 5
Height: 10
Perimeter: 30
New Width: 7
New Height: 12
New Perimeter: 38

Объяснение вывода

  • Мы создаем прямоугольник с шириной 5 и высотой 10, а затем вычисляем его периметр. Изменяя ширину и высоту, мы получаем обновленные значения и периметр.


Свойства и их связь с другими атрибутами

Теперь давайте рассмотрим, как использовать свойства для вычисления значений на основе других атрибутов. Мы создадим класс Person, у которого будет полное имя и свойство для получения его инициалов.

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def initials(self):
        return f"{self.first_name[0].upper()}.{self.last_name[0].upper()}."


 

Создание объекта и использование класса Person

Теперь создадим объект класса Person и протестируем его свойства.

person = Person("John", "Doe")
print("Full Name:", f"{person.first_name} {person.last_name}")  # Полное имя
print("Initials:", person.initials)                             # Инициалы

Вывод:

Full Name: John Doe
Initials: J.D.

Объяснение вывода

  • Мы создаем объект Person с именем "John" и фамилией "Doe". Затем, используя свойство initials, мы получаем инициалы, которые формируются из первых букв имени и фамилии.

 

Таблица по декораторам property без примеров:

ПунктОписание
Что такое декоратор propertyДекоратор property позволяет управлять доступом к атрибутам класса, создавая свойства (геттеры и сеттеры).
Основное назначениеИнкапсуляция логики доступа к атрибутам, добавление контроля над их значениями.
СинтаксисИспользуется синтаксис @property для определения геттера и @имя_свойства.setter для сеттера.
ГеттерМетод, который возвращает значение атрибута, предоставляя доступ к атрибуту через свойство.
СеттерМетод, который устанавливает значение атрибута, обеспечивая контроль над присваиваемым значением.
Вычисляемые свойстваСвойства, которые вычисляются на основе других атрибутов, позволяя возвращать значения без хранения их в отдельной переменной.
Обработка ошибокВозможность проверки условий (например, положительное значение) в методах-сеттерах.
Связь с другими атрибутамиСвойства могут зависеть от значений других атрибутов, позволяя реализовать взаимосвязи между ними.
Преимущества использованияУлучшение структуры кода, возможность добавления логики доступа к атрибутам без изменения их интерфейса.

 

Декоратор property в Python — мощный инструмент, позволяющий управлять доступом к атрибутам класса и инкапсулировать логику, связанную с ними. Мы рассмотрели несколько примеров использования декоратора property, включая геттеры, сеттеры и вычисляемые свойства. Теперь вы можете использовать декоратор property в своих проектах, чтобы улучшить структуру и читаемость кода. Если у вас есть дополнительные вопросы или вы хотите рассмотреть более сложные примеры, не стесняйтесь спрашивать!

Перейти к следующему шагу

Комментарии