Reputation: 125
My IDE has "corrected" my code to convert a function (and other code) to a property. I am worried that it might be inefficient.
@property
def output_all_children(self):
lh = ListHolder()
traverse_directories(self.start_directory, lh)
return lh.internal_list
this does some heavy I/O lifting and takes some time. I am wondering now if this is incorrect due to efficiency reasons. I am wondering if the results aren't cached like I hoping would happen.
If this property is accessed several times is it going to rebuild and return the lh.internal_list every time? I would correct this by having a class level variable and updating it when self.start_directory is changed.
I have looked at this: How to create decorator for lazy initialization of a property and it refers to a read-only property while mine would be a updated property
Please no comments on trusting IDEs blindly. I know and that thinking prompted this question.
Upvotes: 2
Views: 707
Reputation: 184191
Indeed, the property will be called each time. This isn't a good use of a property; callers will expect it to take not much longer than an attribute access.
There's a lovely decorator in Pyramid called reify
that calls the function the first time, then sets the value as a same-named attribute on the object so that the previously-calculated value is used the next time.
If you don't want to use Pyramid just for the one decorator, you can use this version I wrote (it works like Pyramid's but I wrote it independently):
import functools
def reify(func):
class Descriptor(object):
def __get__(self, inst, type=None):
val = func(inst)
setattr(inst, func.__name__, val)
return val
return functools.wraps(func)(Descriptor())
To make it work nicely with your use case, just del
the attribute off the instance when you need it to be recalculated:
@reify
def all_children(self):
lh = ListHolder()
traverse_directories(self.start_directory, lh)
return lh.internal_list
# for internal use only, call when a cached all_children may no longer be valid
def _invalidate_all_children(self):
try:
del self.all_children
except AttributeError:
pass
Upvotes: 1
Reputation: 1122082
No, a property merely translates attribute access into a function call. No automatic caching takes place.
In other words, the syntax instance.output_all_children
is translated into instance.output_all_children()
for you.
You can easily add some caching to a property method, or you can reuse the code you link to in your question; merely replace the __set__
method to handle attribute assignment, if you so desire.
Adding some caching:
_output_all_children = None
@property
def output_all_children(self):
if self._output_all_children is None:
lh = ListHolder()
traverse_directories(self.start_directory, lh)
self._output_all_children = lh.internal_list
return self._output_all_children
Upvotes: 3