Python - unexpected imports

I hope someone can provide some insight into some of the additional name bindings that Python3 creates during import. Here's a test case:

I created a test package called "spam" (original, I know). It contains 3 files as follows:

enter image description here

The content of the files looks like this:

...

__ __ INIT ru:

from .foo import Foo  
from .bar import Bar  

      

foo.py:

def Foo():
    pass

      

bar.py:

def Bar():  
    pass  

      

Pretty simple stuff. When I import the "spam" package, I see that it creates name bindings to the Foo () and Bar () functions in the "spam" namespace, which are expected... It is also not expected to bind the name to the "foo" and "bar" modules in the "spam" namespace as shown below.

enter image description here

What's even more interesting is that these additional module name bindings do not occur if I import the Foo () and Bar () functions in main , as shown below:

enter image description here

Reading the documentation for the import statement (language abstract and tutorial), I don't see anything that could cause this.

Can anyone shed some light on why when importing a function from a module within a package, it also associates a name with the module containing the function?

+3


source to share


1 answer


Yes - that's right, and part of the Python import mechanism.

There is a lot going on when importing a module, but we can focus on a few:

1) Python checks if the module is loaded - this means that it checks if it is qualifying ban (name with dots) is under sys.modules

2) If not, it actually loads the module: it includes checking for pre-compiled cached bytecode files, parsing, compiling the .py file otherwise, etc.

3) It actually concatenates the names as they are in the command import

: "from .foo import Foo" creates a variable "Foo" in the current namespace that points to "spam.foo.Foo".



Suppose a module is always loaded as a whole and associated with it in the sys.modules dictionary. Also, the import process makes all submodules available in the module namespace visible in that package - this is what makes the names "foo" and "bar" visible in your spam package.

At the end of your file, __init__.py

you can remove the names "foo" and "bar", but that will break the way expected imports and uses of spam.foo work in fundamental ways - basically: sys.modules["spam.foo"]

will exist, but sys.modules["spam"].foo

wont - means that after one tries to do:

import spam.foo
spam.foo.Foo()

      

Python will give a name error on "foo".

The imported technique will report this as existing (it's in sys.modules), so it doesn't do anything. But "spam.foo" has been removed, so it cannot be reached.

0


source







All Articles