4

Suppose there are two files a.py and b.py. Inside them are the following:

# a.py
from b import foo

foo()

# b.py
import os

def foo():
    print(os.cwd())

This works just fine, but why is a.py unable to see the imports of b.py implicitly for its own use:

# a.py
import b

def bar():
    print(os.cpu_count())  # fails

b.foo()
bar()

# b.py
import os

def foo():
    print(os.cwd())

I am reading the import system docs but haven't been able to figure this out. The os import exists in the namespace of b.py but it's not available to a.py and I am unsure why. The globals() method is at the module level, so given the rules, is there no sharing of imports between modules (without using __all__ and * imports)? Does each module get its own import namespace?

  • 3
    `os` is only imported inside of `b`. That means that `os` is local to that file. However, because you've imported `b`, you can access `os` using `b.os`. Unless you issue a star-import like: `from b import *` then they will have their own namespaces. – Hampus Larsson Feb 19 '21 at 16:22
  • Each module has its own namespace _period_. `import` does two things: It pulls a module into the import cache if it isn't already there, _and_ adds references (pointing into that cache) to the current module's `__globals__` (aka that current module's namespace). – Charles Duffy Feb 19 '21 at 17:11
  • @HampusLarsson, ...sure, `from os import *` means that you aren't adding a reference into `os` from the current module's namespace (but instead adding separate references to each element in `os.__all__`), but that doesn't mean it's any less true that the module still has its own namespace, which is how I interpret your last sentence. – Charles Duffy Feb 19 '21 at 17:13

1 Answers1

2

Each module has its namespace. The import statement does two things: if a module is not yet imported in the current process, it is loaded and executed. After that its name is bound to the local namespace where the import statement is.

If a module already had been imported in the same process, it is not reloaded, but the names in the import statement are correctly bound by the import statement.

The correct thing to do is to add another import os on your a module - but if you do import b you can use b.os.cwd() (as "os" is a bound name in the module b after it is executed)

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • And in fact, `os` is one of the modules typically created by the interpreter on startup, so `import os` virtually *always* only creates a new name in the local namespace bound to the module accessbile via `sys.modules['os']`. – chepner Feb 19 '21 at 18:38