Каков правильный шаблон в 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
.