Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

import in python 3, explain the output please

Contents :

tutorials/maindir/
├── dir1
│   ├── file11.py
│   ├── file12.py
│   ├── __init__.py
│   └── __pycache__
│       ├── file11.cpython-36.pyc
│       └── __init__.cpython-36.pyc
├── dir2
│   ├── file21.py
│   ├── file22.py
│   └── __init__.py
├── file1.py
├── file2.py
├── __init__.py
└── __pycache__
    ├── file1.cpython-36.pyc
    └── __init__.cpython-36.pyc

now I have a code giving me following output which I don't get.

Code ::

print("--main--", dir())
from tutorials.maindir import *
print("--main--", dir())

OutPut ::

--main-- ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
tutorials
tutorials.maindir
tutorials.maindir.dir1
--main-- ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'dir1', 'tutorials']

I don't get why dir1 and tutorials are there in the dir()'s output.

maindir.__init__.py 's Code :

print(__name__)
import tutorials.maindir.dir1.file11

EDIT 1 ::

So if the code is :

print("--main--", dir())
import tutorials.maindir.dir1.file11
print("--main--", dir())

The output is :

--main-- ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
tutorials
tutorials.maindir
tutorials.maindir.dir1
--main-- ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'tutorials']
like image 951
Shashank Singh Avatar asked Sep 12 '19 09:09

Shashank Singh


Video Answer


1 Answers

When you run an import you are causing __init__.py to run. [source]

If __all__ is not defined, the statement from sound.effects import * does not import all submodules from the package sound.effects into the current namespace; it only ensures that the package sound.effects has been imported (possibly running any initialization code in __init__.py) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by __init__.py. It also includes any submodules of the package that were explicitly loaded by previous import statements.

In your case, this includes another import:

import tutorials.maindir.dir1.file11

That causes certain things to be bound to the local namespace: [source]

define a name or names in the local namespace for the scope where the import statement occurs.

And more specifically:

If the module being imported is not a top level module, then the name of the top level package that contains the module is bound in the local namespace as a reference to the top level package. The imported module must be accessed using its full qualified name rather than directly

So in your case, tutorials is the top level module which was already imported, and dir1 is the imported package (chosen by being the parent name).

And then finally dir() is going to: [source]

Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.

So to your comment, the top level package and the individual names defined in the import statements are bound to the local namespace which is why they show up, but maindir is not a top level package or an imported package so it does not show up.


EDIT: (and additional information)

First, I'd recommend doing some technical reading on a few things if all of this is super interesting to you or relevant:

  • PEP 420 -- Implicit Namespace Packages
  • PEP 302 -- New Import Hooks
  • PEP 328 -- Imports: Multi-Line and Absolute/Relative

Those PEPs discuss a lot of how modern import works and some of the special and weird parts of it, as well as how to form it to do what you need it to do in your use case.


On to your edit question:

When the name variable is of the form package.module, normally, the top-level package (the name up till the first dot) is returned, not the module named by name. However, when a non-empty fromlist argument is given, the module named by name is returned. [source]

So for your basic long import statement, it will put the top level module into the namespace.

If the list of identifiers is replaced by a star ('*'), all public names defined in the module are bound in the local namespace for the scope where the import statement occurs. [source]

import name gives you name
import name.one gives you name
import name.one.two gives you name
from name import one gives you one
from name import one as two gives you two

Now back to your example:
from tutorials.maindir import * will first import the top level module. Then __init__.py will run which does another import. That import will skip the top level module since that is already imported, and will move down to the package and then import as the name of the parent, or essentially the right most . it will move left one term and import as that name. [code].

like image 101
MyNameIsCaleb Avatar answered Oct 28 '22 16:10

MyNameIsCaleb