Python memoising/deferred lookup property decorator
For all sorts of great utilities I'm using boltons.
As part of that library you have cachedproperty:
from boltons.cacheutils import cachedproperty
class Foo(object):
def __init__(self):
self.value = 4
@cachedproperty
def cached_prop(self):
self.value += 1
return self.value
f = Foo()
print(f.value) # initial value
print(f.cached_prop) # cached property is calculated
f.value = 1
print(f.cached_prop) # same value for the cached property - it isn't calculated again
print(f.value) # the backing value is different (it's essentially unrelated value)
Applying a decorator to a property getter
prop = property(memoized(prop.fget), prop.fset, prop.fdel, prop.__doc__)
Don't modify the property; make a new one.
Lazy class property decorator
Pyramid framework has a very nice decorator called reify
, but it only works at instance level, and you want class level, so let's modify it a bit
class class_reify(object):
def __init__(self, wrapped):
self.wrapped = wrapped
try:
self.__doc__ = wrapped.__doc__
except: # pragma: no cover
pass
# original sets the attributes on the instance
# def __get__(self, inst, objtype=None):
# if inst is None:
# return self
# val = self.wrapped(inst)
# setattr(inst, self.wrapped.__name__, val)
# return val
# ignore the instance, and just set them on the class
# if called on a class, inst is None and objtype is the class
# if called on an instance, inst is the instance, and objtype
# the class
def __get__(self, inst, objtype=None):
# ask the value from the wrapped object, giving it
# our class
val = self.wrapped(objtype)
# and set the attribute directly to the class, thereby
# avoiding the descriptor to be called multiple times
setattr(objtype, self.wrapped.__name__, val)
# and return the calculated value
return val
class Test(object):
@class_reify
def foo(cls):
print("foo called for class", cls)
return 42
print(Test.foo)
print(Test.foo)
Run the program and it prints
foo called for class <class '__main__.Test'>
42
42
How to create decorator for lazy initialization of a property
Denis Otkidach's CachedAttribute is a method decorator which makes attributes lazy (computed once, accessible many). To make it also read-only, I added a __set__
method. To retain the ability to recalculate (see below) I added a __delete__
method:
class ReadOnlyCachedAttribute(object):
'''Computes attribute value and caches it in the instance.
Source: Python Cookbook
Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
This decorator allows you to create a property which can be computed once and
accessed many times. Sort of like memoization
'''
def __init__(self, method, name=None):
self.method = method
self.name = name or method.__name__
self.__doc__ = method.__doc__
def __get__(self, inst, cls):
if inst is None:
return self
elif self.name in inst.__dict__:
return inst.__dict__[self.name]
else:
result = self.method(inst)
inst.__dict__[self.name]=result
return result
def __set__(self, inst, value):
raise AttributeError("This property is read-only")
def __delete__(self,inst):
del inst.__dict__[self.name]
For example:
if __name__=='__main__':
class Foo(object):
@ReadOnlyCachedAttribute
# @read_only_lazyprop
def bar(self):
print 'Calculating self.bar'
return 42
foo=Foo()
print(foo.bar)
# Calculating self.bar
# 42
print(foo.bar)
# 42
try:
foo.bar=1
except AttributeError as err:
print(err)
# This property is read-only
del(foo.bar)
print(foo.bar)
# Calculating self.bar
# 42
One of the beautiful things about CachedAttribute
(and
ReadOnlyCachedAttribute) is that if you del foo.bar
, then the next time you
access foo.bar
, the value is re-calculated. (This magic is made possible by
the fact that del foo.bar
removes 'bar'
from foo.__dict__
but the propertybar
remains in Foo.__dict__
.)
If you don't need or don't want this ability to recalculate,
then the following (based on Mike Boers' lazyprop) is a simpler way to make a read-only lazy property.
def read_only_lazyprop(fn):
attr_name = '_lazy_' + fn.__name__
@property
def _lazyprop(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
@_lazyprop.setter
def _lazyprop(self,value):
raise AttributeError("This property is read-only")
return _lazyprop
Accessor decorator that only runs decorated code once in Typescript, returns previously calculated value on subsequent calls
I believe we can compute the value the first time and store it, and then retrieve it on subsequent calls using reflect-metadata
as follows.
import "reflect-metadata";
const metadataKey = Symbol("initialized");
function once(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const getter = descriptor.get!;
descriptor.get = function () {
const val = Reflect.getMetadata(metadataKey, target, propertyKey);
if (val) {
return val;
}
const newValue = getter.apply(this);
Reflect.defineMetadata(metadataKey, newValue, target, propertyKey);
return newValue;
};
}
A working example can be found here.
Big thanks to OP for helping to get the kinks worked out.
Related Topics
How to Remove Packages Installed with Python's Easy_Install
Peak-Finding Algorithm for Python/Scipy
Convert a List of Characters into a String
Is a Python Dictionary an Example of a Hash Table
Determine If Python Is Running Inside Virtualenv
How to Implement Server Push in Flask Framework
Do Python for Loops Work by Reference
How to Do Exponentiation in Python
Combine Lists with Common Elements
Find Column Name in Pandas That Matches an Array
Numpy Sum Elements in Array Based on Its Value
How to Use Qscrollarea to Make Scrollbars Appear
Filling in Login Forms in Instagram Using Selenium and Webdriver (Chrome) Python Osx
Separation of Business Logic and Data Access in Django