4

Linked questions:

If an import statement is inside a function, will the memory occupied by it get reclaimed once the function exits? If yes, is the timing of the reclamation deterministic (or even -ish)?

def func():
    import os
    ...
    # function about to exit; will memory occupied by `os` be freed?

If anyone has knowledge on the behavior of micropython on this topic, bonus points.

Bob
  • 4,576
  • 7
  • 39
  • 107
  • 4
    `import` loads the specified module, that only happens once per program, and isn't undone if you leave a scope in which an import happened. Otherwise how would a module work? So: no. Please note also that if you write a question that gets closed as a duplicate and you think that it shouldn't be considered as such, you should *edit that question* rather than asking a new one. – jonrsharpe Oct 18 '17 at 15:25
  • 4
    @jonrsharpe this is different. Previous one was w.r.t. correctness or style. – Bob Oct 18 '17 at 15:29
  • You definitely can't use the import outside of the function it is imported in, so it would seem odd to me that it remains in memory. – bendl Oct 18 '17 at 15:33
  • 1
    @bendl Ran a few tests and indeed the `os` name is not in scope outside the function (unless you add `global os`). However, after calling a function that imports a module the reference does appear to remain in `sys.modules` (`os` is a bad example since it seems to be loaded by default anyway). – ryachza Oct 18 '17 at 15:53
  • @bendl that's because of the scope of the name, the loaded module itself is still stored in the module dictionary. – jonrsharpe Oct 18 '17 at 15:54
  • Short answer: no – juanpa.arrivillaga Oct 18 '17 at 15:55
  • Yep after a few tests of my own I've got that the module remains in `sys.modules`... Now this is getting out of my knowledge of Python, but the garbage collector wouldn't get rid of that module object just because the reference has gone out of scope if it's still referenced in `sys.modules`, right? – bendl Oct 18 '17 at 15:57

2 Answers2

3

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.

BlackJack
  • 4,476
  • 1
  • 20
  • 25
0

To second what @BlackJack wrote, per Python semantics, an "import" statement adds module reference to sys.modules, that alone does keep the module object from being garbage collected.

You can try to do del sys.modules["some_module"], but there's no guarantee that all memory taken by the module would be reclaimed. (That issue popped up previously, but I don't remember the current state of it, e.g. if bytecode objects can be garbage-collected).

If yes, is the timing of the reclamation deterministic (or even -ish)?

In MicroPython, "reclamation time" is guaranteedly non-deterministic, because it uses purely garbage collection scheme, no reference counting. That means that any resource-consuming objects (files, sockets) should be closed explicitly.

Otherwise, function-level imports are valid and useful idiom in Python, and especially useful in MicroPython. It allows to import some module only if a particular code path is hit. E.g. if user never calls some function, a module will not be imported, saving more memory for tasks user needs more in this particular application/invocation.

pfalcon
  • 6,724
  • 4
  • 35
  • 43
  • 1
    The official [Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) disagrees about function level imports. Under [Imports](https://www.python.org/dev/peps/pep-0008/#imports) it says _„Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.“_ The benefit to see all the dependencies at the top outweighs the usually negligible potential memory savings. I would think hard before violating this expectation from the reader, and clearly document this at the top where the reader expects the imports. – BlackJack Nov 01 '17 at 11:07
  • Good for them. People who want to import modules conditionally, import modules conditionally. (Obviously, people who aren't really sure whether they should import modules conditionally, shouldn't do that.) – pfalcon Nov 01 '17 at 15:06