8

I was reading about Default Parameter Values in Python on Effbot.

There is a section later in the article where the author talks about Valid uses for mutable defaults and cites the following example:

and, for highly optimized code, local rebinding of global names:

import math

def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
    ...

I haven't been able to locate how this causes fast/highly optimised execution of code. Can somebody enlighten on this with a well informed (and preferably with citations) answer?

Nandeep Mali
  • 4,456
  • 1
  • 25
  • 34
  • Because it does the lookup of e.g. `math.cos` only once, at definition time. After that, it's found locally. Otherwise, the interpreter has to go looking through locals, nonlocals, imports every time `math.cos` is called. – jonrsharpe Aug 28 '14 at 13:56
  • possible duplicate of [Why does Python code run faster in a function?](http://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function) – roippi Aug 28 '14 at 14:02
  • @roippi: I don't think so. The answers to both questions happen to match, but the questions are quite different. – georg Aug 28 '14 at 14:06
  • @georg the answers happen to match because the same underlying question is being asked - why are global lookups slower than local ones. – roippi Aug 28 '14 at 14:08
  • 1
    @roippi I couldn't locate that question when I was looking to ask mine. Agreed the answers are same, but it might help to keep this around so I don't have to *interpret* the *underlying question*. Also, am targeting a specific approach (of local rebinding) in this question. Anyway. Whatever call the community makes. – Nandeep Mali Aug 28 '14 at 14:12
  • 1
    @roippi: sure, but I personally only dupvote strictly identical questions (up to the wording), not "conceptually similar". – georg Aug 28 '14 at 14:14
  • 3
    Dupes are not some sort of punishment for being unable to find your answer. They are helpful signposts that point people who are having the same problem (but similarly don't know exactly what to search for) to a high-quality canonical answer. – roippi Aug 28 '14 at 14:21
  • 1
    This might not help but as a citation to check if local rebinding is actually fast: https://gist.github.com/keshavagrawal89/aeb439130dfa12e1e58a Check the time difference. – Keshav Agrawal Aug 28 '14 at 15:11
  • @roippi Alright. Wasn't aware that dupes are not deleted but a reference is just added on top. – Nandeep Mali Aug 28 '14 at 16:33

1 Answers1

8

CPython access to local variable is index-based (involving the LOAD_FAST opcode).

On the other hands, globals are accessed through name lookup in a dictionary (using opcode LOAD_GLOBAL). For module variables, it's a two step process. Using a first look-up (LOAD_GLOBAL) to push the module object, and then using a second look-up (LOAD_ATTR) to locate the appropriate member.

Even if dictionary lookup is highly optimized, it can't beat indirect access.

import math
def f():
    math.sin(1)

  4           0 LOAD_GLOBAL              0 (math)   ***
              3 LOAD_ATTR                1 (sin)    ***
              6 LOAD_CONST               1 (1)
              9 CALL_FUNCTION            1
             12 POP_TOP             
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE


from math import sin
def f():
    sin(1)

  4           0 LOAD_GLOBAL              0 (sin)    ***
              3 LOAD_CONST               1 (1)
              6 CALL_FUNCTION            1
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE


def f(sin=math.sin):
    sin(1)


  7           0 LOAD_FAST                0 (sin)    ***
              3 LOAD_CONST               1 (1)      
              6 CALL_FUNCTION            1
              9 POP_TOP             
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE  
Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125
  • 1
    There are actually two improvements here: the local variable, and the one-time lookup from the `math` namespace. You can see the difference by using `from math import sin` and examining `def h(): sin(1)`. It still uses `LOAD_GLOBAL` to find `sin`, but improves over `f` by not needing `LOAD_ATTR`. – chepner Aug 28 '14 at 14:26
  • @chepner Yeah. I tried to say that _" For module variables, you might even have several dictionary lookups"_ but wasn't able to express it clearly. Thank you for your comment! – Sylvain Leroux Aug 28 '14 at 14:32