Python import mechanism is always a myth for me. Sometimes importing a package can grant access to the modules underneath it. For instance,
import urllib
urllib.parse.unquote
gives
<function urllib.parse.unquote>
which shows functions are accessible even with only the package (i.e. urllib in this case) imported but not down to the module file. This is done within Jupyter notebook.
But when I do the same thing in terminal
>>> import urllib
>>> urllib.parse.unquote
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'urllib' has no attribute 'parse'
Both Python versions are 3.6.1.
What makes the difference and what is the good practice?
EDIT to combine the answers by @user2357112 and @Tomoki.
Directly from @user2357112
For an access to urllib.parse to work, the following two conditions must be true:
The
urllibmodule object must be bound to theurllibname, whether in the local, global, or some enclosing scope. Theurllib.parsesubmodule must have been initialized and bound to the parse attribute of theurllibmodule object. An importurllibin the current local or global scope (or any enclosing scope) satisfies the first condition.An
import urllib.parseexecuted anywhere in the program satisfies the second condition, since it loads the submodule and binds it to theparseattribute on theurllibmodule object, and there's only oneurllibmodule object for the whole program.In the environments where
urllib.parsewas accessible after a simple importurllib, some other code must have loadedurllib.parse, causing you to see it.
The evidence is provided by @Tomoki
Test: "import IPython"
└─IPython:┐
┌────┘
├──"from core.application import Application"
│ └──IPython.core.application: "from IPython.core import release, crashhandler"
│ └──IPython.core.crashhandler: "from IPython.core import ultratb"
│ └──IPython.core.ultratb: "import pydoc"
│ └──pydoc: "import urllib.parse"
└──"from terminal.embed import embed"
└──IPython.terminal.embed:┐
┌───────────┘
├──"from IPython.core import magic_arguments"
│ └──IPython.core.magic_arguments: "from IPython.utils.text import dedent"
│ └──IPython.utils.text: "from pathlib import Path"
│ └──pathlib: "from urllib.parse import quote_from_bytes"
├──"from IPython.core.magic import Magics, magics_class, line_magic"
│ └──IPython.core.magic: "from IPython.core import oinspect"
│ └──IPython.core.oinspect: "from IPython.core import page"
│ └──IPython.core.page: "from IPython.core.display import display"
│ └──IPython.core.display: "import mimetypes"
│ └──mimetypes: "import urllib.parse"
└──"from IPython.terminal.interactiveshell import TerminalInteractiveShell"
└──pygments.plugin: "import pkg_resources"
└──pkg_resources: "import email.parser"
└──email.parser: "from email.feedparser import FeedParser, BytesFeedParser"
└──email.feedparser: "from email._policybase import compat32"
└──email._policybase: "from email.utils import _has_surrogates"
└──email.utils: "import urllib.parse"
The last line indeed touches urllib.parse.
Another Evidence
import scipy does not provide access to scipy.stats.norm in either a terminal or Jupyter notebook because none of the environment touches scipy.stats at all.
What is the good practice?
We can conclude from above that it is not only a good practice, but in fact a requirement to ##import the whole module levels##.
"Always import down to file (module) level to guarantee the access"
Thanks all for the answers!