0

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)
Eduardo
  • 687
  • 1
  • 6
  • 24

2 Answers2

3

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
Community
  • 1
  • 1
erik258
  • 14,701
  • 2
  • 25
  • 31
  • Thanks Dan. Maybe I can use a variable to store the result? or just call the method to obtain the returned value? – Eduardo Jan 08 '17 at 17:30
  • Thanks a lot Dan. Sorry, my ignorance, but, what 'hasattr' do? Check if the attribute is already instanciated? – Eduardo Jan 08 '17 at 17:39
  • Wow, this is very useful. Thanks again for your help. – Eduardo Jan 08 '17 at 17:52
  • I saw the post to which you referred. I have to ask. 'getattr' works this case? – Eduardo Jan 08 '17 at 21:37
  • 1
    It's a good question - the thing you're only going to run into more of, as you learn and grow as a programmer. This might be a good opportunity for you to start doing your own research into these things. Why not try to read the links, do some searches, and make some experiments yourself? You'll find it's the quickest way to learn. – erik258 Jan 09 '17 at 05:55
  • Dan, I edited my question, what do you think about the new approach? – Eduardo Jul 09 '17 at 20:55
0

Your m_in_maj method is called in the m_est_maj method:

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

Instead, give it an optional argument, and call m_in_maj only if this argument is not passed:

def m_est_maj(self, x=None):
    if x is None:
        x = self.m_in_maj()
    return x / ((self.hw + 2 * self.tf) / 2)

Then, when calling m_est_maj:

x = A.m_in_maj()
print(x, A.m_est_maj(x))
Right leg
  • 16,080
  • 7
  • 48
  • 81
  • Thanks, I´ll try this method. – Eduardo Jan 08 '17 at 17:32
  • i would argue that logic like that belongs inside the class. You've taken logic _out_ of the class and delegated it to the _caller_, which breaks the encapsulation you get in the class. – erik258 Jan 08 '17 at 17:36
  • @DanFarrell I would agree in another situation, but there is no encapsulation in Python. Your solution forces an encapsulation, which might be good, though generally unneeded. My solution lets more control to the caller. These are two different approaches, but I do not think one is better than the other, it just depends on the situation. – Right leg Jan 08 '17 at 17:46