Is there a performance cost putting python imports inside functions?

后端 未结 3 1626
隐瞒了意图╮
隐瞒了意图╮ 2021-02-04 00:53

I build quite complex python apps, often with Django. To simplify inter-application interfaces I sometimes use service.py modules that abstract away from the models.

As

3条回答
  •  迷失自我
    2021-02-04 01:22

    The point at which you import a module is not expected to cause a performance penalty, if that's what you're worried about. Modules are singletons and will not be imported every single time an import statement is encountered. However, how you do the import, and subsequent attribute lookups, does have an impact.

    For example, if you import math and then every time you need to use the sin(...) function you have to do math.sin(...), this will generally be slower than doing from math import sin and using sin(...) directly as the system does not have to keep looking up the function name within the module.

    This lookup-penalty applies to anything that is accessed using the dot . and will be particularly noticeable in a loop. It's therefore advisable to get a local reference to something you might need to use/invoke frequently in a performance critical loop/section.

    For example, using the original import math example, right before a critical loop, you could do something like this:

    # ... within some function
    sin = math.sin
    for i in range(0, REALLY_BIG_NUMBER):
        x = sin(i)   # faster than:  x = math.sin(x)
        # ...
    

    This is a trivial example, but note that you could do something similar with methods on other objects (e.g. lists, dictionaries, etc).

    I'm probably a bit more concerned about the circular imports you mention. If your intention is to "fix" circular imports by moving the import statements into more "local" places (e.g. within a specific function, or block of code, etc) you probably have a deeper issue that you need to address.

    Personally, I'd keep the imports at the top of the module as it's normally done. Straying away from that pattern for no good reason is likely to make your code more difficult to go through because the dependencies of your module will not be immediately apparent (i.e. there're import statements scattered throughout the code instead of in a single location).

    It might also make the circular dependency issue you seem to be having more difficult to debug and easier to fall into. After all, if the module is not listed above, someone might happily think your module A has no dependency on module B and then up adding an import A in B when A already has import B hidden in some deep dark corner.


    Benchmark Sample

    Here's a benchmark using the lookup notation:

    >>> timeit('for i in range(0, 10000): x = math.sin(i)', setup='import math', number=50000)
    89.7203312900001
    

    And another benchmark not using the lookup notation:

    >>> timeit('for i in range(0, 10000): x = sin(i)', setup='from math import sin', number=50000)
    78.27029322999988
    

    Here there's a 10+ second difference.

    Note that your gain depends on how much time the program spends running this code --i.e. a performance critical section instead of sporadic function calls.

提交回复
热议问题