Making a LazilyEvaluatedConstantProperty class in Python
There's a little thing I want to do in Python, similar to the built-in property
, that I'm not sure how to do.
I call this class LazilyEvaluatedConstantProperty
. It is intended for properties that should be calculated only once and do not change, but they should be created lazily rather than on object creation, for performance.
Here's the usage:
开发者_开发知识库class MyObject(object):
# ... Regular definitions here
def _get_personality(self):
# Time consuming process that creates a personality for this object.
print('Calculating personality...')
time.sleep(5)
return 'Nice person'
personality = LazilyEvaluatedConstantProperty(_get_personality)
You can see that the usage is similar to property
, except there's only a getter, and no setter or deleter.
The intention is that on the first access to my_object.personality
, the _get_personality
method will be called, and then the result will be cached and _get_personality
will never be called again for this object.
What is my problem with implementing this? I want to do something a bit tricky to improve performance: I want that after the first access and _get_personality
call, personality
will become a data attribute of the object, so lookup will be faster on subsequent calls. But I don't know how it's possible since I don't have a reference to the object.
Does anyone have an idea?
I implemented it:
class CachedProperty(object):
'''
A property that is calculated (a) lazily and (b) only once for an object.
Usage:
class MyObject(object):
# ... Regular definitions here
def _get_personality(self):
print('Calculating personality...')
time.sleep(5) # Time consuming process that creates personality
return 'Nice person'
personality = CachedProperty(_get_personality)
'''
def __init__(self, getter, name=None):
'''
Construct the cached property.
You may optionally pass in the name that this property has in the
class; This will save a bit of processing later.
'''
self.getter = getter
self.our_name = name
def __get__(self, obj, our_type=None):
if obj is None:
# We're being accessed from the class itself, not from an object
return self
value = self.getter(obj)
if not self.our_name:
if not our_type:
our_type = type(obj)
(self.our_name,) = (key for (key, value) in
vars(our_type).iteritems()
if value is self)
setattr(obj, self.our_name, value)
return value
For the future, the maintained implementation could probably be found here:
https://github.com/cool-RR/GarlicSim/blob/master/garlicsim/garlicsim/general_misc/caching/cached_property.py
精彩评论