О свойствах (property) в Python

В Python, есть такая штука, как свойства (property) — это объекты, которые реализуют descriptor-протокол и позволяют проделывать следующее:

class Person(object):

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

    @property
    def full_name(self):
        return " ".join([self.first_name, self.last_name])

person = Person("Andrey", "Popp")
person.full_name # "Andrey Popp"

Это реализация attribute getter/setter шаблона. То есть, обращаюсь к full_name как к аттрибуту, мы неявно вызываем соответствующий метод и его результат как бы становится значением аттрибута. Почему “как бы”? Потому что он будет заново вычисляться при каждом обращении к full_name.

Я стараюсь никогда не использовать свойства, в тех случаях, когда для их вычисления приходится производить какие-то побочные эффекты, будь-то I/O или даже просто изменение какой-нибудь структуры данных.

Операция обращения к аттрибуту (а именно так выглядит работа с property) по своей семантике должна быть “дешёвой”. Со свойствами можно наворотить следующее — представьте себе, что вы, находясь в консоли Python и обращаетесь к некоему аттрибуту myobj.some_info, в результате чего отправляется запрос в БД и вычисление свойства происходит в течении n секунд — крайне неприятно.

Одна из библиотек, с которыми я работал и которая не следует вышеупомянутой рекомендации — SQLAlchemy. Там можно настроить маппинг таблиц на модели таким образом, что при обращении к свойствам последних их значения будут запрашиваться из БД отдельным запросом. Получается если обратиться к такому свойству в цикле из 100 итераций, то будет сделано 100 отдельных запросов к БД и при этом, программист читающий код, может этого совершенно не заметить.

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

P.S. Кстати, аналогичное суждение верно и в языках на подобии Scala, где можно опустить () при вызове функций, которые не принимают никаких параметров — я считаю, что если функция производит некие побочные эффекты, то () после её вызова необходимо оставить.