In Python, What Happens When You Import Inside of a Function

In Python, what happens when you import inside of a function?

Does it re-import every time the function is run?

No; or rather, Python modules are essentially cached every time they are imported, so importing a second (or third, or fourth...) time doesn't actually force them to go through the whole import process again. 1

Does it import once at the beginning whether or not the function is run?

No, it is only imported if and when the function is executed. 2, 3

As for the benefits: it depends, I guess. If you may only run a function very rarely and don't need the module imported anywhere else, it may be beneficial to only import it in that function. Or if there is a name clash or other reason you don't want the module or symbols from the module available everywhere, you may only want to import it in a specific function. (Of course, there's always from my_module import my_function as f for those cases.)

In general practice, it's probably not that beneficial. In fact, most Python style guides encourage programmers to place all imports at the beginning of the module file.

Is it pythonic to import inside functions?

In the long run I think you'll appreciate having most of your imports at the top of the file, that way you can tell at a glance how complicated your module is by what it needs to import.

If I'm adding new code to an existing file I'll usually do the import where it's needed and then if the code stays I'll make things more permanent by moving the import line to the top of the file.

One other point, I prefer to get an ImportError exception before any code is run -- as a sanity check, so that's another reason to import at the top.

I use pyChecker to check for unused modules.

Should import statements always be at the top of a module?

Module importing is quite fast, but not instant. This means that:

  • Putting the imports at the top of the module is fine, because it's a trivial cost that's only paid once.
  • Putting the imports within a function will cause calls to that function to take longer.

So if you care about efficiency, put the imports at the top. Only move them into a function if your profiling shows that would help (you did profile to see where best to improve performance, right??)


The best reasons I've seen to perform lazy imports are:

  • Optional library support. If your code has multiple paths that use different libraries, don't break if an optional library is not installed.
  • In the __init__.py of a plugin, which might be imported but not actually used. Examples are Bazaar plugins, which use bzrlib's lazy-loading framework.

If I import a module inside a function, will the variables be local?

Yes, the module will be local to the function, at least in the example above(I am using Python 3.6).

Example:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
def test():
... import math
... s = math.cos(1)
...
g = math.cos(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined

Import statement inside class/function definition - is it a good idea?

It's the most common style to put every import at the top of the file. PEP 8 recommends it, which is a good reason to do it to start with. But that's not a whim, it has advantages (although not critical enough to make everything else a crime). It allows finding all imports at a glance, as opposed to looking through the whole file. It also ensures everything is imported before any other code (which may depend on some imports) is executed. NameErrors are usually easy to resolve, but they can be annoying.

There's no (significant) namespace pollution to be avoided by keeping the module in a smaller scope, since all you add is the actual module (no, import * doesn't count and probably shouldn't be used anyway). Inside functions, you'd import again on every call (not really harmful since everything is imported once, but uncalled for).

import inside a function: is memory reclaimed upon function exit?

The first import executes the code in the module. It creates the module object's attributes. Each subsequent import just references the module object created by the first import.

Module objects in Python are effectively singletons. For this to work, the Python implementation has to keep the one and only module instance around after the first import, regardless of the name the module was bound to. If it was bound to a name anyway, as there are also imports of the form from some_module import some_name.

So no, the memory isn't reclaimed.

No idea about Micropython, but I would be surprised if it changes semantics here that drastically. You can simply test this yourself:

some_module.py:

value = 0

some_other_module.py:

def f():
import some_module
some_module.value += 1
print(some_module.value)

f()
f()

This should print the numbers 1 and 2.

Advantage of importing libraries inside function?

Actually, importing the modules locally inside a function would improve efficiency (slightly). Looking up local names is always faster than looking up globals because the global namespace is one of the last that Python checks when searching for a name.

That said, I wouldn't recommend doing this for three reasons:

  1. It wastes lines of code. Every function which needs a particular module will have to import it. This means that you will have a lot of repeated import statements.

  2. It makes the dependencies of your code hard to find. One of the main reasons that you import at the top of a file is so that the imports are easily visible.

  3. It goes against PEP 0008, which specifically says:

    Imports are always put at the top of the file, just after any module
    comments and docstrings, and before module globals and constants.

Is there a performance cost putting python imports inside functions?

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.

Importing a function inside another function

A nested function function_2 is an object in the local namespace of the outer function. It doesn't even exist as a function when function_1 is not running.

Normally, a nested function either performs a private computation for the outer function, or gets returned in some form or another. In the latter case, you can run function_1 to get a reference to function_2.

Any object that you want to import should be in the global namespace, or at least directly referenceable from it. A better design would be

x = 3

def function_1(x):
y = x + 7
# Do some stuff, including using function_2
return y

def function_2(value):
# Do some stuff
return value

And keep in mind that defining a function is not the same as running it. As a corollary, the x inside function_1 is not the same as the x that you set to 3 at the module level.

Update for your update

You absolutely can make function_1 into a class, and make function_2 a method of it:

class function_1:
def __call__(self, x):
y = x + 7
value = self.function_2(y)
return y, value

@staticmethod
def function_2(value):
return value

In this case you could do the following in your other script:

from test import function_1

f1 = function_1()
# Call it:
f1(7)
# Call function_2:
function_1.function_2('blah')
# Or alternatively:
f1.function_2('foo')

Importing modules that have all its functions inside a main() function

main() typically calls your other functions but does not contain them. Your other functions will lie in the body of the script above main() and can be called in the standard way.

So your test.py example could look like this:

def my_function():
print('Hello')

def my_function2(num):
return num*num

def main():
my_function()
my_function2(5)

if __name__ == "__main__": # if module not imported
main()


Related Topics



Leave a reply



Submit