Каков правильный шаблон в Python для реализации ленивых геттеров?

иногда мне нравится писать атрибуты геттера для объекта таким образом, что при первом вызове они выполняются один раз, и это значение сохраняется и возвращается при будущих вызовах. В objective-c я бы использовал ivar или статическую переменную для хранения этого значения. Что-то вроде:

- (id)foo
{
    if ( _foo == nil )
    {
        _foo = // hard work to figure out foo
    }
    return _foo
}

этот же шаблон хорошо держится в Python, или есть более приемлемый способ сделать это? До сих пор у меня было практически то же самое. Что мне не нравится в моем решении, так это то, что мой объект загроможден значениями и геттерами для этих значений:

def foo(self):
    if not hasattr(self, "mFoo":
        self.mFoo = # heavy lifting to compute foo
    return self.mFoo

4 ответов


использовать ленивый собственность вместо. Геттеры Итак, 1990-е.


вместо того, чтобы делать явный тест "hasattr" каждый раз, пусть среда выполнения Python сделает это за вас. Определить __getattr__ в вашем классе, который вызывается только при ссылке на неопределенный атрибут.

class Sth(object):
    @property
    def a(self):
        print "property a"
        return self._a

    def _a_compute(self):
        # put expensive computation code here
        print "_a_compute"
        return 1000

    def __getattr__(self, attr):
        print "__getattr__"
        if attr == '_a':
            self._a = self._a_compute()
            return self._a


ss = Sth()
print "first time"
print ss.a
print
print "second time"
print ss.a

печатает следующее:

first time
property a
__getattr__
_a_compute
1000

second time
property a
1000

вы могли бы оставить собственность и __getattr__ тест для " a "напрямую, но тогда у вас не будет видимости" a " в качестве атрибута в dir для таких вещей, как интроспекция или автозаполнение IDE.


Я бы сделал что-то вроде этого:

@property
def foo(self):
    return self._foo_value if hasattr(self, '_foo_value') else calculate_foo()

def calculate_foo(self):
    self._foo_value = # heavy foo calculation
    return self._foo_value

теперь вы можете получить доступ к "foo", если он уже был рассчитан или нет, используя:

object.foo

вы можете использовать точно такой же рисунок в Python. Вы, кажется, беспокоитесь о том, нужно ли делать my_object.get_foo() все время обновления. К счастью, Python дает вам хороший инструмент для работы здесь в виде свойства:

class my_class(object):

     @property
     def foo(self):
       # calculate if needed
       return self._foo

Это позволяет вам иметь то, что используется как атрибут, даже если он реализован как функция. т. е. пользователи будут делать my_object.foo, и не заботится о том, что его функция работает за кулисами.

в другое дело, что соглашение Python говорит, что частные атрибуты пишутся _foo, а не mFoo.