Нашли ошибку?
Скачать чистый
урок 3 из 4≈ 20 минупражнения: 0 из 3

Сеттер и проверка значений

@x.setter разрешает запись в свойство и проверяет значение: неверное — raise ValueError. Проверка в __init__ через свойство и рекурсия в сеттере.

Python запустится при первом выполнении

Свойство только для чтения не даёт изменить атрибут через публичный интерфейс. Но цену товара менять нужно — просто не на любое значение: отрицательная цена — ошибка в программе, и лучше узнать о ней сразу. Для этого у свойства бывает вторая половина — сеттер (setter): метод, который вызывается при записи в свойство.

Сеттер

Сеттер объявляется вторым методом с тем же именем и декоратором @имя.setter. Он получает новое значение, проверяет его и записывает во внутренний атрибут:

[ ]Объём с проверкой
class Cup:
    def __init__(self, volume):
        self._volume = volume

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, value):
        print(f"сеттер получил {value}")
        if value < 50:
            print("слишком мало — объём не изменён")
            return
        self._volume = value


cup = Cup(300)
cup.volume = 400  # вызывается сеттер
print(cup.volume)
cup.volume = 10
print(cup.volume)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
сеттер получил 400
400
сеттер получил 10
слишком мало — объём не изменён
400

raise: сообщить об ошибке

Что делать, если значение неверное? Молча проигнорировать — плохо: программа продолжит работать с неверными данными, и ошибка всплывёт далеко отсюда. Правильно — выбросить исключение (raise) — так же, как это делает Python, когда вы пишете int("сто"):

[ ]Неверный объём — ValueErrorошибка: ValueError
class Cup:
    def __init__(self, volume):
        self._volume = volume

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, value):
        if value < 50:
            raise ValueError(f"объём {value} мл — меньше 50 мл не бывает")
        self._volume = value


cup = Cup(300)
cup.volume = 10
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
ValueError: объём 10 мл — меньше 50 мл не бывает

raise ValueError("…") сразу прерывает метод: строки ниже не выполняются, атрибут не меняется, а вызвавший код получает исключение. ValueError — встроенное исключение для «значение подходящего типа, но недопустимое». Сообщение пишут так, чтобы было понятно, что исправить.

Вызывающий код может перехватить исключение в try/except и решить, что делать дальше. Запись except ValueError as error даёт перехваченному исключению имя: str(error) — его сообщение.

[ ]Перехватить ValueError
try:
    cup.volume = 10
    print("записали")  # не выполнится
except ValueError as error:
    print("не получилось:", error)
print(cup.volume)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
не получилось: объём 10 мл — меньше 50 мл не бывает
300
[ ]Упражнение 1Цена с проверкой решено

Допишите класс Product: свойство price возвращает _price, а сеттер записывает новую цену, если она больше нуля, иначе выбрасывает ValueError с сообщением "цена должна быть больше нуля". Неудачная попытка не меняет старую цену.

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price  # запись через свойство: сработает сеттер

    @property
    def price(self):
        ...

    @price.setter
    def price(self, value):
        ...

Проверка при создании

Сеттер срабатывает при любой записи в свойство — и в __init__ тоже. Если в __init__ написать self.volume = volume (без подчёркивания), неверный экземпляр просто не создастся:

[ ]Проверка в __init__ через свойствоошибка: ValueError
class Cup:
    def __init__(self, volume):
        self.volume = volume  # через свойство — с проверкой

    @property
    def volume(self):
        return self._volume

    @volume.setter
    def volume(self, value):
        if value < 50:
            raise ValueError(f"объём {value} мл — меньше 50 мл не бывает")
        self._volume = value


tiny = Cup(20)
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
ValueError: объём 20 мл — меньше 50 мл не бывает
Вопрос

В __init__ класса с сеттером цены написано self._price = price. Что будет при Product("Чай", -5)?

[ ]Упражнение 2Оценка в отзыве решено

Объявите класс Review: Review("Анна", 5) хранит имя автора в атрибуте author и оценку в свойстве stars. Оценка — число от 1 до 5; число вне этого диапазона — и при создании, и при изменении — выбрасывает ValueError.

class Review:
    def __init__(self, author, stars):
        ...

Неверные данные на входе

Проверки в сеттерах особенно полезны, когда данные приходят извне — из файла или формы. Объект сам не даст создать себя с ошибкой, а программа перехватит исключение и пропустит плохую строку.

[ ]Упражнение 3Загрузка прайса решено

В rows — строки прайса «название, цена», среди них есть ошибочные. Создайте список products из экземпляров Product (из упражнения про цену) для верных строк, а в skipped сохраните названия пропущенных — тех, где Product(...) выбросил ValueError.

rows = [("Эспрессо", 150), ("Капучино", -220), ("Чай", 120), ("Раф", 0), ("Какао", 190)]
products = ...
skipped = ...

Нашли ошибку?