18

Say I have a function in Python that uses a constant computed float value like 1/3.

def div_by_3(x):
    return x * (1/3)

If I call the function repeatedly, will the value of 1/3 be automatically cached for efficiency? Or do I have to do something manually such as the following?

def div_by_3(x, _ONE_THIRD=1/3):
    return x * _ONE_THIRD
3
  • 1
    The latter, although you could keep the constant as a module level global variable instead of passing it the the function (unless you want to pass it as a parameter). Commented Oct 7, 2015 at 19:11
  • I think doing it as a parameter is faster (avoids looking up the global name). Commented Oct 7, 2015 at 19:25
  • Nope. No difference in speed, and less clear to the user of the function if it is not a parameter. Commented Oct 7, 2015 at 19:32

1 Answer 1

16

Find out for yourself! The dis module is great for inspecting this sort of stuff:

>>> from dis import dis
>>> def div_by_3(x):
...     return x * (1/3.)
... 
>>> dis(div_by_3)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 LOAD_CONST               2 (3.0)
              9 BINARY_DIVIDE       
             10 BINARY_MULTIPLY     
             11 RETURN_VALUE        

As you can see, the 1/3 calculation happens every time. (Note: I changed 3 to 3. to force float division, otherwise it'd just be 0. You can also enable future-division, which actually changed the behavior, see edit section below).

And your second approach:

>>> def db3(x, _ONE_THIRD=1/3.):
...   return x * _ONE_THIRD
... 
>>> dis(db3)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_FAST                1 (_ONE_THIRD)
              6 BINARY_MULTIPLY     
              7 RETURN_VALUE        

More information on the second can be found by inspecting the function object:

>>> inspect.getargspec(db3)
ArgSpec(args=['x', '_ONE_THIRD'], varargs=None, keywords=None, defaults=(0.3333333333333333,))

You can see the default value is cached in there.

EDIT: Turns out this is a little more interesting -- in Python 3 they do get cached (and also in Python 2.7 when you enable from __future__ import division):

>>> dis.dis(div_by_3)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               3 (0.3333333333333333)
              6 BINARY_MULTIPLY
              7 RETURN_VALUE

Switching to integer division (//) in either Python 3 or 2.7-with-future-division doesn't change this, it just alters the constant to be a 0 instead of 0.333.. Also, using integer division directly in 2.7 without future-division will cache the 0 as well.

Learned something new today!

Sign up to request clarification or add additional context in comments.

6 Comments

How can you explain second approach? Where is division?
Is the default value for the keyword argument calculated every time the function is called?
@Monkey No, default values are calculated once (on function definition) and stored on the created function object.
@user3990145 The division happened during function definition and the resultant value was stored on the function object. When the function is called, all that's left to do is multiply that with x.
Note that for Python 3, the peephole optimizer does precompute the 1/3 constant. (CPython specific, of course.)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.