Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







Recipe 20.4. Caching Attribute Values


Credit: Denis S. Otkidach


Problem


You want to be able to compute
attribute values, either per instance or per class, on demand, with
automatic caching.


Solution


Custom descriptors are the right tools for this task:

class CachedAttribute(object):
''' Computes attribute value and caches it in the instance. '''
def _ _init_ _(self, method, name=None):
# record the unbound-method and the name
self.method = method
self.name = name or method._ _name_ _
def _ _get_ _(self, inst, cls):
if inst is None:
# instance attribute accessed on class, return self
return self
# compute, cache and return the instance's attribute value
result = self.method(inst)
setattr(inst, self.name, result)
return result
class CachedClassAttribute(CachedAttribute):
''' Computes attribute value and caches it in the class. '''
def _ _get_ _(self, inst, cls):
# just delegate to CachedAttribute, with 'cls' as ``instance''
return super(CachedClassAttribute, self)._ _get_ _(cls, cls)


Discussion


If your class instances have attributes that must be computed on
demand but don't generally change after
they're first computed, custom descriptor
CachedAttribute as presented in this recipe is just
the ticket. Here is a toy example of use (with Python 2.4 syntax):

class MyObject(object):
def _ _init_ _(self, n):
self.n = n
@CachedAttribute
def square(self):
return self.n * self.n
m = MyObject(23)
print vars(m) # 'square' not there yet
# emits: {'n': 23}
print m.square # ...so it gets computed
# emits: 529
print vars(m) # 'square' IS there now
# emits: {'square': 529, 'n': 23}
del m.square # flushing the cache
print vars(m) # 'square' removed
# emits: {'n': 23}
m.n = 42
print vars(m)
# emits: {'n': 42} # still no 'square'
print m.square # ...so gets recomputed
# emits: 1764
print vars(m) # 'square' IS there again
# emits: {'square': 1764, 'n': 23}

As you see, after the first access to m.square, the
square attribute is cached in instance
m, so it will not get recomputed for that
instance. If you need to flush the cache, for example, to change
m.n, so that m.square will get
recomputed if it is ever accessed again, just del
m.square
. Remember, attributes can be removed in Python! To
use this code in Python 2.3, remove the decorator syntax
@CachedAttribute and insert instead an assignment
square = CachedAttribute(square)
after the end of the def
statement for method square.

Custom descriptor CachedClassAttribute is just a
simple variant of CachedAttribute, easily obtained
by inheritance: it computes the value by calling a method on the
class rather than the instance, and it caches the result on the
class, too. This may help when all instances of the class need to see
the same cached value. CachedClassAttribute is
mostly meant for cases in which you do not need to flush the cache
because its _ _get_ _ method usually wipes away
the instance descriptor itself:

class MyClass(object):
class_attr = 23
@CachedClassAttribute
def square(cls):
return cls.class_attr * cls.class_attr
x = MyClass( )
y = MyClass( )
print x.square
# emits: 529
print y.square
# emits: 529
del MyClass.square
print x.square # raises an AttributeError exception

However, when you do need a cached class attribute with the ability
to occasionally flush it, you can still get it with a little trick.
To implement this snippet so it works as intended, just add the
statement:

class MyClass(MyClass): pass

right after the end of the class MyClass statement
and before generating any instance of MyClass. Now,
two class objects are named MyClass, a hidden
"base" one that always holds the
custom descriptor instance, and an outer
"subclass" one that is used for
everything else, including making instances and holding the cached
value if any. Whether this trick is a reasonable one or whether
it's too cute and clever for its own good, is a
judgment call you can make for yourself! Perhaps it would be clearer
to name the base class MyClassBase and use
class MyClass(MyClassBase), rather than use the
same name for both classes; the mechanism would work in exactly the
same fashion, since it is not dependent on the names of classes.


See Also


Custom descriptors are best documented at Raymond
Hettinger's web page: http://users.rcn.com/python/download/Descriptor.


/ 394