СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Методическая разработка «Декоратор property в Python»

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Методическая разработка «Декоратор property в Python»»

МУНИЦИПАЛЬНОЕ АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ДОПОЛНИТЕЛЬНОГО ОБРАЗОВАНИЯ

«ЦЕНТР ДЕТСКОГО ТЕХНИЧЕСКОГО ТВОРЧЕСТВА»













Методическая разработка

«Декоратор property в Python»

к дополнительной общеобразовательной

общеразвивающей программе

технической направленности

«Программирование на Python»



Возраст детей: 10-17 лет









Автор: Костычев Вадим Александрович










г. Заречный Пензенской области

2025 г.

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


Цель: научить использовать декоратор @property для реализации контролируемого доступа к атрибутам класса, заменяя геттеры и сеттеры на читаемый и безопасный подход, основанный на принципах инкапсуляции и обратной совместимости.


Задачи:

  1. Объяснить философию @property как альтернативы геттерам/сеттерам.

  2. Разобрать синтаксис геттера, сеттера и делитера.

  3. Научить создавать вычисляемые свойства и избегать рекурсии.

  4. Отработать валидацию данных через @setter.

  5. Показать применение @deleter в реальных сценариях.

  6. Научить обеспечивать обратную совместимость при изменении интерфейса.



«В Python не нужны геттеры и сеттеры — пока они не понадобятся. А когда понадобятся — используйте @property» — правило Python-разработчиков


Зачем нужен @property?


В ООП часто возникает потребность:

- читать значение атрибута (геттер),

- устанавливать его с проверкой (сеттер),

- вычислять значение на лету (вычисляемое свойство).


В языках вроде Java или C# для этого используют геттеры (getBalance()) и сеттеры (setBalance()):


// Java-пример

public class BankAccount {

private double balance;


public double getBalance() { return balance; }

public void setBalance(double amount) {

if (amount Баланс не может быть отрицательным!");

this.balance = amount;

}

}



Но в Python это неудобно:


# Анти-паттерн: геттеры/сеттеры в Python


class BankAccount:

def __init__(self, balance):

self._balance = balance


def get_balance(self):

return self._balance


def set_balance(self, amount):

if amount

raise ValueError("Баланс не может быть отрицательным!")

self._balance = amount


acc = BankAccount(1000)

print(acc.get_balance()) # Неуклюже

acc.set_balance(1500) # Неестественно



Что делает @property?


Он превращает метод в атрибут, который можно читать и (опционально) записывать как обычное поле, но при этом с сохранением логики!


class BankAccount:

def __init__(self, balance):

self._balance = balance


@property

def balance(self): # Геттер

return self._balance


@balance.setter # Сеттер

def balance(self, amount):

if amount

raise ValueError("Баланс не может быть отрицательным!")

self._balance = amount


acc = BankAccount(1000)

print(acc.balance) # Как у атрибута!

acc.balance = 1500 # Как у атрибута — но с проверкой!



Ключевая идея: @property позволяет изменить внутреннюю реализацию класса без изменения внешнего интерфейса — даже если пользователь уже использовал .balance как атрибут.


Основные компоненты @property


Компонент

Назначение

Синтаксис

Геттер

Читает значение

@property

Сеттер

Записывает значение с проверкой

@имя_свойства.setter

Делитер

Удаляет значение

@имя_свойства.deleter


Геттер (@property)


Превращает метод в читаемое свойство.


Пример:


class Circle:

def __init__(self, radius):

self.radius = radius


@property

def area(self): # Геттер

return 3.14159 * self.radius 2


c = Circle(5)

print(c.area) # 78.53975 — как атрибут!

# print(c.area()) Ошибка — это не метод!


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


Когда использовать?

- Когда значение можно вычислить из других атрибутов.

- Когда нужно скрыть сложность вычисления.

- Когда вы хотите отказаться от хранения значения и считать его на лету (экономия памяти).



Сеттер (@имя.setter)


Позволяет устанавливать значение с валидацией, преобразованием или побочными эффектами.


Пример:


class Temperature:

def __init__(self, celsius):

self._celsius = celsius


@property

def celsius(self):

return self._celsius


@celsius.setter

def celsius(self, value):

if value

raise ValueError("Температура не может быть ниже абсолютного нуля!")

self._celsius = value


t = Temperature(25)

t.celsius = 30 # OK

t.celsius = -300 # ValueError: Температура не может быть ниже...



Преимущества:

- Обратная совместимость: если раньше было obj.temp = 20, то после добавления @property код продолжает работать.

- Валидация: нельзя установить некорректное значение.

- Автоматическое обновление зависимых данных:


class Temperature:

def __init__(self, celsius):

self._celsius = celsius

self._fahrenheit = None # кэш


@property

def celsius(self):

return self._celsius


@celsius.setter

def celsius(self, value):

if value

raise ValueError("Недопустимая температура!")

self._celsius = value

self._fahrenheit = None # сброс кэша — пересчитаем при следующем запросе


@property

def fahrenheit(self):

if self._fahrenheit is None:

self._fahrenheit = (self._celsius * 9/5) + 32

return self._fahrenheit



Делитер (@имя.deleter)


Позволяет определить поведение при удалении свойства через del.


Пример:


class Person:

def __init__(self, name):

self._name = name


@property

def name(self):

return self._name


@name.setter

def name(self, value):

if not isinstance(value, str) or len(value.strip()) == 0:

raise ValueError("Имя должно быть непустой строкой")

self._name = value.strip()


@name.deleter

def name(self):

print(f" Имя '{self._name}' удалено.")

self._name = "Аноним"


p = Person("Alice")

print(p.name) # Alice

del p.name # Выводит: Имя 'Alice' удалено.

print(p.name) # Аноним


- del obj.prop вызывает @prop.deleter, но не удаляет атрибут из __dict__ — он просто выполняет ваш код.



Полный пример: Класс с @property, @setter, @deleter


python

class Student:

def __init__(self, name, grade):

self.name = name # через setter

self._grade = None

self.grade = grade # через setter


@property

def name(self):

return self._name


@name.setter

def name(self, value):

if not isinstance(value, str) or len(value.strip()) == 0:

raise ValueError("Имя должно быть непустой строкой")

self._name = value.strip().title()


@property

def grade(self):

return self._grade


@grade.setter

def grade(self, value):

if not isinstance(value, (int, float)) or not (0

raise ValueError("Оценка должна быть числом от 0 до 100")

self._grade = value


@grade.deleter

def grade(self):

print(f"🗑️ Оценка студента {self.name} сброшена.")

self._grade = None


@property

def letter_grade(self):

"""Вычисляемое свойство: буквенная оценка"""

if self._grade is None:

return "Не оценено"

elif self._grade = 90:

return "A"

elif self._grade = 80:

return "B"

elif self._grade = 70:

return "C"

elif self._grade = 60:

return "D"

else:

return "F"


# Использование

s = Student("alice smith", 87)

print(s.name) # Alice Smith

print(s.grade) # 87

print(s.letter_grade) # B


s.grade = 95

print(s.letter_grade) # A


del s.grade # Вывод: Оценка студента Alice Smith сброшена.

print(s.grade) # None

print(s.letter_grade) # Не оценено


# s.grade = 150 # ValueError

# s.name = "" # ValueError



Почему @property лучше, чем геттеры/сеттеры?


Критерий

Геттер/сеттер (get_name())

@property

Синтаксис

obj.get_name()

obj.name

Читаемость

Многословно

Естественно

Инкапсуляция

Есть

Есть, лучше

Обратная совместимость

Ломается при изменении

Сохраняется

Стиль Python

Не рекомендуется

Рекомендуется PEP 8

Возможность вычисляемых свойств

Нет

Да



Если вы начинаете с obj.x, и потом решаете добавить валидацию — просто добавьте @property и @x.setter. Все существующие вызовы obj.x продолжат работать — никто ничего не сломает.


Типичные ошибки и как их избежать


Ошибка

Почему не стоит использовать

Как исправить

@property без _-атрибута

self.value внутри getter - бесконечная рекурсия

Всегда используйте скрытый атрибут: self._value

@property с return self.property

Рекурсия - RecursionError

return self._property

Использование @property для простого чтения без причины

Излишняя сложность

Делайте атрибут публичным: self.value

Отсутствие валидации в сеттере

Можно установить неверные данные

Всегда проверяйте входные данные в @setter

Использование @property для тяжёлых операций

Замедлит код, если используется часто

Добавьте кэширование (if self._cached is None: ...)


### Плохой пример:


class BadExample:

@property

def x(self):

return self.x # Бесконечная рекурсия!


→ RecursionError: maximum recursion depth exceeded


### Правильный пример:


class GoodExample:

def __init__(self):

self._x = 0


@property

def x(self):

return self._x


@x.setter

def x(self, value):

self._x = value



Когда НЕ использовать @property?


Ситуация

Рекомендация

Атрибут — просто данные, без логики

Делайте его публичным: self.color = "red"

Вы пишете быстрый скрипт

Не усложняйте

Вам нужна производительность в цикле

@property немного медленнее — но разница ничтожна

Вы делаете API для внешних систем

Используйте @property — это чистый и стандартный способ


Начинайте с публичных атрибутов. Добавляйте @property только тогда, когда вам нужно добавить логику.


Python не требует от вас писать геттеры. Но если вы захотите — вы можете добавить их без изменения кода клиентов.


@property — это не просто декоратор. Это культурный символ Python: «Начни просто. Стань мощнее, не меняя интерфейс. Будь ответственным. Не ломай другим».