@property: вычисляемые атрибуты
@property превращает метод в атрибут без скобок: площадь пересчитывается сама, сеттер проверяет данные. Разбираем, когда свойство излишне.
Редакция Питоники
В уроке про инкапсуляцию баланс прятался за методами get_balance() и deposit(). Схема рабочая, но многословная: чтение — метод со скобками, запись — другой метод. В Python есть путь элегантнее: @property превращает метод в атрибут, который вычисляется при обращении, а его напарник-сеттер перехватывает присваивание. Снаружи — обычное поле, внутри — код с проверками.
Свойство: метод без скобок
Декоратор @property навешивается на метод без параметров, кроме self, — и его значение читается как атрибут: rect.area вместо rect.area(). Свойство удобно для вычисляемых полей: площадь из сторон, полное имя из имени и фамилии, возраст из даты рождения. Хранить их отдельно не стоит — легко рассинхронизировать; свойство пересчитывается при каждом обращении и потому не врёт. Свойство выглядит как поле, а работает как код: обращение к rect.area выполняет вычисление заново.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
rect = Rectangle(4, 5)
print(rect.area)
rect.width = 10
print(rect.area)
20 50
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
@property
def full_name(self):
return f"{self.first} {self.last}"
author = Person("Лев", "Толстой")
print(author.full_name)
Лев Толстой
Сеттер: присваивание с проверкой
Свойство без сеттера только читается: попытка rect.area = 100 упадёт с AttributeError — и это хорошо: площадь не должна назначаться извне. Для перезаписываемых полей добавляется сеттер: @celsius.setter перехватывает присваивание t.celsius = ... и решает, принимать ли значение. Внутри сеттера живёт валидация — данные не попадают в объект некорректными.
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
print("Ниже абсолютного нуля не бывает")
return
self._celsius = value
t = Temperature(25)
print(t.celsius)
t.celsius = -300
t.celsius = -30
print(t.celsius)
25 Ниже абсолютного нуля не бывает -30
| Задача | Методы в стиле get/set | С property |
|---|---|---|
| получить площадь | rect.get_area() | rect.area |
| записать с проверкой | rect.set_width(10) | rect.width = 10 |
| вычисляемое поле | метод со скобками | атрибут без скобок |
| смена реализации | интерфейс держится на методах | поле заменяется свойством незаметно |
Свойства завершают картину инкапсуляции: данные спрятаны в _полях, а наружу смотрят проверяемые атрибуты. Дальше: наследование — потомки расширяют классы через super(); super() и MRO — как Python ищет метод в иерархии. В финальном проекте курса property примет на себя все проверки.
Сначала предскажи ответ в голове — это главный навык программиста.
class Square:
def __init__(self, side):
self.side = side
@property
def perimeter(self):
return self.side * 4
sq = Square(3)
sq.side = 10
print(sq.perimeter)
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
@property
def initials(self):
return f"{self.first[0]}.{self.last[0]}."
p = Person("Агния", "Барто")
print(p.initials)
class Age:
def __init__(self, value):
self.value = value
@property
def value(self):
return self._value
@value.setter
def value(self, new):
self._value = max(new, 0)
age = Age(30)
age.value = -5
print(age.value)
1. Как прочитать свойство area?
2. Зачем нужен @x.setter?
3. Когда вычисляется свойство area = width * height?
4. Почему в сеттере пишут self._celsius, а не self.celsius?
5. Что произойдёт при присваивании свойству без сеттера?
6. Когда property не нужен?
Создайте класс Cube (куб): __init__ принимает side. Добавьте сеттер для side, отвергающий неположительные значения (печатает «Ребро должно быть положительным» и не меняет поле), и свойство surface, возвращающее площадь поверхности как 6 * side ** 2. Создайте куб с ребром 3 и выведите площадь, попробуйте задать side = -2, затем задайте side = 5 и снова выведите площадь.
Что делает @property в Python?
Превращает метод в свойство: значение вычисляется при каждом обращении, но читается как атрибут, без скобок — rect.area вместо rect.area(). Используется для вычисляемых полей и для подмены простого атрибута логикой без смены интерфейса.
Как сделать атрибут «только для чтения»?
Объявите @property без парного сеттера: чтение работает, присваивание падает с AttributeError. Так поступают с производными значениями вроде площади — их нельзя назначить извне, потому что они зависят от других полей.
Чем property лучше методов get_area() и set_width()?
Интерфейсом: obj.area читается как поле, obj.width = 10 — как присваивание. Код вызывающей стороны не меняется, даже если внутри вы замените простое поле вычислением или добавите валидацию. Пары методов get/set в стиле Java в Python считаются шумом.
Урок 5: инкапсуляцияПочему property хранит данные в _celsius, а не в celsius?
Потому что имена свойства и закрытого поля должны различаться: запись self.celsius = value внутри сеттера celsius снова вызовет тот же сеттер — бесконечную рекурсию и RecursionError. Договорённость: публичное имя — свойство, _имя — место хранения.
Понравился урок? Сошлитесь на него
«Свойство выглядит как поле, а работает как код: обращение к rect.area выполняет вычисление заново.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что читать дальше
ООП · Урок 5
Инкапсуляция: _protected и __private
Одно подчёркивание — договорённость, два — искажение имени. Учимся прятать детали класса за публичным интерфейсом и понимать name mangling.
ООП · Урок 7
Наследование: расширяем классы
Наследование — способ расширять классы без переписывания: потомок получает всё от родителя, переопределяет и дополняет через super(). Родство проверяет isinstance.
ООП · Урок 2
__init__ и self: как рождается объект
__init__ задаёт объекту стартовое состояние, а self — это сам объект внутри его методов. Разбираем механику вызова hero.heal(30) до последней детали.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
BeautifulSoup / Scrapy · Урок 4
Извлечение данных: text, get_text, атрибуты и навигация по дереву
Найти элемент — полдела: данные лежат в тексте и атрибутах. Разбираем text против string, безопасное чтение атрибутов и ходьбу по дереву от родителя к соседям.
beautifulsoup получить атрибут href ссылкиатрибуты html python
FastAPI · Урок 4
Модели Pydantic: тело запроса и автоматическая валидация
Описываем данные моделями Pydantic: автоконверсия типов, правила Field, вложенные модели — и валидируем тело POST-запроса FastAPI.
pydantic валидациявалидация тела запроса fastapi
ООП · Урок 3
Атрибуты класса и атрибуты экземпляра
Атрибут класса один на всех, атрибут экземпляра — личный. Разбираем порядок поиска атрибутов и классическую ловушку общего списка — с исправлением.
атрибуты класса pythonатрибут экземпляра python