Python(3.3)包如何工作?

时间:2014-01-14 09:51:18

标签: python python-3.x

我正在使用Python 3.3,在Windows上测试它。我不明白。我为什么这样做:

>>> import urllib

我收到错误

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

>>> dir(urllib)
['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', 
 '__loader__', '__name__', '__package__', '__path__']

没有请求,所以看起来很稳固。但是在导入子模块请求时:

>>> import urllib.request

似乎有效

>>> urllib.request
<module 'urllib.request' from 'C:\\Python33\\lib\\urllib\\request.py'>

现在自动dir(urllib)显示:

>>> dir(urllib)
['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', 
 '__loader__', '__name__', '__package__', '__path__', 'error', 'parse', 
 'request', 'response']

为什么我看不到所有子模块import urllib之后?根据{{​​3}},它应该是requesterrorparseparserobots。它在其他操作系统中有所不同吗?

3 个答案:

答案 0 :(得分:2)

当你

>>> import package
>>> package.something

您真正在做的是寻找something中定义的名称package/__init__.py。因此,如果某人明确地在那里放了一些名字,你可以使用它否则你就不能。

例如,查看Python web.py库(github)。你可以

>>> import web  # which is a package
>>> web.httpserver  # which is a module located in web/httpserver.py
<module 'web.httpserver' ...>

它在这里,因为在web/__init__.py中有明确的import httpserver或类似声明。

实际上它不是Python 3的问题。你在Python 3中遇到它,因为它使用标准库的包而不仅仅是像Python 2那样的普通模块。urllib/__init__.py由于某种原因而currently empty因此你需要明确地import精确的模块像request一样使用它们。对于一些标准软件包Python 3 import一些&#34;子模块&#34;在相应的__init__.py个文件中。也许它只是为了向后兼容。

另一个问题是为什么在import urllib.request dir(urllib)之后,response显示其他模块,例如urllib.request。看起来importimport urllib.error内部使用的同一包中的所有模块都可用。如果您request error,则sys.modules不会使用它{{1}}。如果你检查Python导入系统的源代码,这一切都变得更容易理解 - 它充满了副作用(例如&#34;缓存&#34;使用全局{{1}}字典导入的模块)。

答案 1 :(得分:0)

urllib是一个包。导入包不会自动导入包中包含的模块;您需要显式导入urllib.request模块。

答案 2 :(得分:0)

让我们看看dir()。如文档中所述,它列出了模块中定义的名称 - 而packages模块是__init__.pyrequest urllib中未定义__init__,因此不会列出。

现在,对于“包如何工作” - 包是某种基于目录的模块。它从__init__.py加载,但允许子模块和子包(与__init__在同一目录中的文件和目录)。现在,当您加载包(例如urllib)时,您不会加载(“加载”意味着“执行其代码”)其中的所有文件和目录,但只有它是“初始化程序”(想想{{1} }作为类的__init__.py方法,但对于模块 - 在最后模块和类都是对象,对吗?)。

与类的比较非常好 - 当你创建对象时,你只调用它的__init__方法,而不是它的所有属性(方法也是属性)。此外,属性可以是函数或字段。字段也可以具有属性。澄清:

  • __init__适用于__init__.py对象的包

  • 子模块用于包对象的哪个方法(函数属性)

  • 子包用于包对象的哪个字段(非函数属性)。

为什么呢? __init__负责设置对象,子模块/方法是在一个地方存储逻辑的方法,子包/字段是组织其他子模块/方法或子子包/字段的方式。

坦率地说:它在CPython实现中不会像这样工作,但它是理解它将如何表现的非常好的模型。