I have a python class with a property that performs an operation on large arrays. What is the best way to store the result of the method after it first was calculated without redoing the operation every time the property is accessed?
For example:
class MyClass:
def __init__(self, x, y):
"""Input variables are two large data arrays"""
self.x = x
self.y = y
@property
def calculate(self):
"""Some computationally expensive operation"""
try:
# the result has been calculated already
return self._result
except AttributeError:
# if not, calculate it
self._result = self.x * self.y
return self._result
Usage:
>>> foo = MyClass(2,3)
>>> a = foo.calculate
>>> print(a)
6
As you can see, all I came up with so far is to have a 'hidden' attribute where the result is stored. Is there a better way to do this? Is the use of @property
correct here?
Thanks.
Python __get__ Magic Method. Python's __get__() magic method defines the dynamic return value when accessing a specific instance and class attribute. It is defined in the attribute's class and not in the class holding the attribute (= the owner class).
Python's magic method __setattr__() implements the built-in setattr() function that takes an object and an attribute name as arguments and removes the attribute from the object. We call this a “Dunder Method” for “Double Underscore Method” (also called “magic method”).
getattr() – This function is used to access the attribute of object. hasattr() – This function is used to check if an attribute exist or not. setattr() – This function is used to set an attribute.
The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.
You need to remember the values for x and y that were used to calculate the cached result. If they change, you need to re-calculate and refresh the cache.
class MyClass:
def __init__(self, x, y):
"""Input variables are two large data arrays"""
self.x = x
self.y = y
self._calculate = None
self._x = None
self._y = None
@property
def calculate(self):
if not self._calculate or self._x != self.x or self._y != self.y:
self._calculate = self.x * self.y
self._x = self.x
self._y = self.x
return self._calculate
>>> a = MyClass(2,3)
>>> print(a.calculate)
6
>>> a.x = 553
>>> print(a.calculate)
1659
The way you've implemented your property is just fine. It works, and has near zero overhead.
As a thought experiment you could take advantage of a specific property of non-data descriptors. A non-data descriptor is similar to property
(which is a data descriptor). A non-data descriptor is an object that defines a __get__
method only. When the instance has an attribute with the same name as a the non-data descriptor this overrides the non-data descriptor (think monkey patching functions). eg.
class cache:
def __init__(self, func):
self.func = func
def __get__(self, obj, type_):
if obj is None:
return self.func
value = self.func(obj)
setattr(obj, self.func.__name__, value)
return value
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
@cache
def calculate(self):
print("calculating")
return self.x + self.y
o = MyClass(1, 2)
print("first", o.calculate) # prints "calcutating" then "first 3"
print("second", o.calculate) # just prints "second 3"
del o.calculate
print("third", o.calculate) # prints "calcutating" then "third 3"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With