为什么导入包有时会授予对其下模块的访问权限,但有时不会?

2022-03-31 00:00:00 python python-import

问题描述

Python导入机制对我来说一直是个神话。有时,导入包可以授予对它下面的模块的访问权限。例如,

import urllib
urllib.parse.unquote

给予

<function urllib.parse.unquote>

,它显示了即使只导入包(即本例中的urllib),但不向下导入到模块文件,也可以访问函数。这是在Jupyter笔记本中完成的。

但当我在终端中做同样的事情时

>>> import urllib
>>> urllib.parse.unquote
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'urllib' has no attribute 'parse'

这两个版本都是3.6.1。

有什么不同?好的做法是什么?

编辑以合并@user2357112和@Tomoki的答案。

直接从@user2357112

要使对urllib.parse的访问起作用,必须满足以下两个条件:

urllib模块对象必须绑定到urllib名称,无论是在本地、全局还是某个封闭作用域中。 urllib.parse子模块必须已初始化并绑定到urllib模块对象的parse属性。 当前本地或全局作用域(或任何封闭作用域)中的导入urllib满足第一个条件。

在程序中的任何位置执行的import urllib.parse满足第二个条件,因为它加载子模块并将其绑定到urllib模块对象上的parse属性,而整个程序只有一个urllib模块对象。

在简单导入urllib后可以访问urllib.parse的环境中,一定已经加载了urllib.parse,使您可以看到它。

证据由@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"

最后一行确实触及urllib.parse

另一个证据

import scipy不提供对终端或Jupyter笔记本中scipy.stats.norm的访问,因为所有环境都不涉及scipy.stats

什么是好做法?

从上面我们可以得出结论,这不仅是一种好的做法,而且实际上是##导入整个模块级别##的要求。

"始终向下导入到文件(模块)级以保证访问"

感谢大家的回答!


解决方案

要访问urllib.parse才能工作,必须满足以下两个条件:

  1. urllib模块对象必须绑定到urllib名称,无论是在本地、全局还是某个封闭作用域中。
  2. urllib.parse子模块必须已初始化并绑定到urllib模块对象的parse属性。

当前本地或全局作用域(或任何封闭作用域)中的import urllib满足第一个条件。

在程序中的任何位置执行的import urllib.parse满足第二个条件,因为它加载子模块并将其绑定到urllib模块对象上的parse属性,而整个程序只有一个urllib模块对象。

在简单的import urllib之后可以访问urllib.parse的环境中,一定有其他代码加载了urllib.parse,使您可以看到它。

相关文章