【问题标题】:import urllib.parse fails when Python run from command line从命令行运行 Python 时导入 urllib.parse 失败
【发布时间】:2016-01-17 08:02:21
【问题描述】:

我在 python 3.4.2 中观察到以下行为,我无法解释。希望有人能对此事有所了解:

在 IPython 中:

In [129]: import urllib

In [130]: print(urllib.parse)
<module 'urllib.parse' from '/Users/ashwin/.pyenv/versions/3.4.2/lib/python3.4/urllib/parse.py'>

我已经导入了一个模块,并打印了它的一个属性。一切都按预期工作。到目前为止,生活是美好的。

现在,我从命令行做同样的事情:

$ python -c 'import urllib; print(urllib.parse)'  
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AttributeError: 'module' object has no attribute 'parse'

说什么?!这不是应该的工作方式。
好的,也许这是 python 范围的行为;使用 -c 标志时,可能不会立即导入模块。让我们尝试另一个模块:

$ python -c 'import datetime; print(datetime.datetime)'
<class 'datetime.datetime'>

什么?!它对datetime 而不是urllib 是如何工作的?我在两个地方都使用相同版本的 python (3.4.2)

有人对此有什么想法吗?

编辑

每一个 cmets:

$ which -a ipython
/Users/ashwin/.pyenv/shims/ipython
/Library/Frameworks/Python.framework/Versions/2.7/bin/ipython
/usr/local/bin/ipython
/usr/local/bin/ipython

$ which -a python
/Users/ashwin/.pyenv/shims/python
/Library/Frameworks/Python.framework/Versions/2.7/bin/python
/usr/bin/python
/usr/bin/python

【问题讨论】:

  • 你可能想要添加which -a ipythonwhich -a python的输出
  • @cel:我不明白这会有什么帮助,但我已经添加了它
  • 所以python -m IPython -c 'import datetime; print(datetime.datetime)'python -c 'import datetime; print(datetime.datetime)' 给出不同的结果?
  • 没有。 python -cdatetime 给出的结果与 python -curllib 不同,前者会抛出 AttributeError 而后者不会。然而,urllib 的代码在交互式 IPython 中使用时不会引发错误
  • 基本上python -c 'import urllib; print(urllib.parse)'python -m IPython -c 'import urllib; print(urllib.parse)' 给出不同的结果。我也看到了。确实是一个非常有趣的观察。我会添加一个常规的 python 标签来增加可见性。

标签: python command-line ipython python-3.4 attributeerror


【解决方案1】:

当您运行import urllib 时,它会创建urllib 模块的模块对象(实际上是package不导入其子模块(解析、请求等)。

如果您想使用属性访问来访问其子模块,则需要将父模块对象 (urllib) 在您的命名空间中。除此之外,该子模块 必须 已经被加载(导入)。来自documentation

如果包spam有一个子模块foo,导入后 spam.foo, spam 将有一个属性 foo 绑定到 子模块。 [...]不变的持有是,如果你有 sys.modules['spam']sys.modules['spam.foo'](如你所愿 在上述导入之后),后者必须作为foo 属性出现 前者。

每个模块只有一个实例,因此对urllib 模块对象(存储在sys.modules['urllib'])所做的任何更改都会在各处反映出来。

您不导入 urllib.parse,但 IPython 可以为了证明这一点,我将创建一个启动文件:

import urllib
print('Running the startup file: ', end='')
try:
    # After importing  'urllib.parse' ANYWHERE,
    # 'urllib' will have the 'parse' attribute.
    # You could also do "import sys; sys.modules['urllib'].parse"
    urllib.parse
except AttributeError:
    print("urllib.parse hasn't been imported yet")
else:
    print('urllib.parse has already been imported')
print('Exiting the startup file.')

并启动ipython

vaultah@base:~$ ipython
Running urllib/parse.py
Running the startup file: urllib.parse has already been imported
Exiting the startup file.
Python 3.6.0a0 (default:089146b8ccc6, Sep 25 2015, 14:16:56) 
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0 -- An enhanced Interactive Python.

是IPython启动时导入pydoc的副作用(which ipython/usr/local/bin/ipython):

/usr/local/bin/ipython, line 7:
  from IPython import start_ipython
/usr/local/lib/python3.6/site-packages/IPython/__init__.py, line 47:
  from .core.application import Application
/usr/local/lib/python3.6/site-packages/IPython/core/application.py, line 24:
  from IPython.core import release, crashhandler
/usr/local/lib/python3.6/site-packages/IPython/core/crashhandler.py, line 28:
  from IPython.core import ultratb
/usr/local/lib/python3.6/site-packages/IPython/core/ultratb.py, line 90:
  import pydoc
/usr/local/lib/python3.6/pydoc.py, line 68:
  import urllib.parse

这解释了为什么下面的代码会失败 - 你只导入 urllib 而似乎没有导入 urllib.parse

$ python -c 'import urllib; print(urllib.parse)'

另一方面,以下命令有效,因为datetime.datetime 不是一个模块。这是一个在import datetime 期间导入的类。

$ python -c 'import datetime; print(datetime.datetime)'

【讨论】:

  • 但是如果我在 IPython 中尝试pydoc,我会得到NameError。这不是说pydoc在启动时没有被IPython导入吗?
  • @inspectorG4dget 它已经被导入了,你可以检查它是否已经在sys.modules'pydoc' in sys.modules返回True。您只需将其导入当前命名空间。
  • 好的,但是 urllib 不在当前命名空间中,或者在 IPython 中的 sys.modules 中。所以我仍然不清楚urllib.parse 是如何自动存在于命名空间中的
  • @inspectorG4dget 我不太确定...从 IPython 运行 [x for x in sys.modules if 'urllib' in x] 会产生 ['urllib.parse', 'urllib']。当import urllib.parse 被执行时,Python 将通过查看sys.modules 来检查它是否可以跳过创建模块对象。当模块对象准备好时,它会以您选择的名称放在本地命名空间中(例如,使用import 语句)。请注意,命名空间不是共享的。
  • 但我不会在 IPython 中尝试 import urllib.parse。我尝试import urllib,然后调用urllib.parse.unquote,没有任何进一步的导入
【解决方案2】:

urllib.parse 从 Python 3 开始可用。我认为您可能需要import urllib.parse,而不是import urllib。不确定(何时)子模块导入是隐式的。

我猜 IPython 在启动时会导入 urllib.parse,这就是它可用的原因。

parse 是一个模块而不是一个属性:

Python 3.4.2 (default, Oct 15 2014, 22:01:37)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import urllib
>>> urllib.parse
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'parse'
>>> import urllib.parse
>>> urllib.parse
<module 'urllib.parse' from '/opt/local/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/urllib/parse.py'>

【讨论】:

  • 您的分析似乎不正确,因为我使用的是 python3.4.2,即使我从命令行调用 python。从我的路径中可以看出,第一个条目来自pyenv,当前指向 v3.4.2
  • 按照建议尝试 import urllib.parse 而不是 import urllib
  • 也感谢您对路径的澄清。我更新了问题并删除了不正确的部分。
  • IPython 在启动时不导入urllib.parse。我知道这一点是因为我自己设置了 IPython 启动导入。但是你是对的,在交互式 shell 中运行它也不能按预期工作。我有一种感觉,IPython 通过创建一个不可见的import urllib.parse 来解决这个问题,以避免这个错误
猜你喜欢
  • 2020-05-14
  • 1970-01-01
  • 2016-12-11
  • 2018-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-24
  • 1970-01-01
相关资源
最近更新 更多