Search code examples
pythonmethodscall

Class method calling twice


I have the following code.

class DobleTSim():
    def __init__(self, bf, hw, tf, tw):
        self.bf = bf
        self.hw = hw  
        self.tf = tf
        self.tw = tw

    def m_in_maj(self):
        print('foo')
        return 2 * (self.bf * self.tf * (self.tf / 2 + self.hw / 2))


    def m_est_maj(self):
        return self.m_in_maj() / ((self.hw + 2 * self.tf) / 2)

A = DobleTSim(200, 500, 12, 12)

print(A.m_in_maj(), A.m_est_maj())

When I execute the code, the output is :

foo
foo
1228800.0 4690.076335877862

How can I avoid execute the method "m_in_maj" twice?

-----EDIT-----

Another solution can be the use of a property and the lru_cache decorator. Is there a disadvantage when using this?.

import functools

class DobleTSim():
    def __init__(self, bf, hw, tf, tw):
        self.bf = bf
        self.hw = hw  
        self.tf = tf
        self.tw = tw

    @property
    @functools.lru_cache()
    def m_in_maj(self):
        print('foo')
        self.a = 2 * (self.bf * self.tf * (self.tf / 2 + self.hw / 2))
        return self.a

    def m_est_maj(self):
        return self.m_in_maj / ((self.hw + 2 * self.tf) / 2)

Solution

  • You call it twice. Once in the print() at the end, once within the call to m_est_maj.

    6 math operations is quite a cheap operation. You probably won't get a meaningful performance increase by caching the result. But you could do so, if you wanted to. You could call m_in_maj from __init__ and save the result to an instance attribute, and refer to that instead of the function call.

    Or you could have the function check to see if the instance attribute was already defined, compute on the first call, and return on subsequent calls.

    This is a pretty common general approach I see around:

    class DobleTSim():
        def __init__(self, bf, hw, tf, tw):
            self.bf = bf
            self.hw = hw
            self.tf = tf
            self.tw = tw
    
        def m_in_maj(self):
            if not hasattr( self, '_m_in_maj_result'):
                print('foo')
                self._m_in_maj_result =  \
                    2 * (self.bf * self.tf * (self.tf / 2 + self.hw / 2))
            return self._m_in_maj_result
    
    
        def m_est_maj(self):
            return self.m_in_maj() / ((self.hw + 2 * self.tf) / 2)
    
    A = DobleTSim(200, 500, 12, 12)
    
    print(A.m_in_maj(), A.m_est_maj())
    

    Based on How to know if an object has an attribute in Python, it might be more pythonic to do something like this:

    def m_in_maj(self):
        try:
            return self._m_in_maj_result
        except AttributeError:
            print('foo')
            self._m_in_maj_result =  \
                2 * (self.bf * self.tf * (self.tf / 2 + self.hw / 2))
            return self._m_in_maj_result