我在python(2.7和3.x)中偶然发现了这个意想不到的行为:
>>> import re as regexp
>>> regexp
<module 're' from '.../re.py'>
>>> from regexp import search
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named 'regexp'
当然from re import search
成功,就像我创建别名之前一样。但是为什么我不能使用别名regexp
作为导入名称的来源,它现在是一个已知的模块?
当模块有多个变体时,这会让你感到讨厌:比如说我还在使用Python 2,我想使用pickle
的C版cPickle
。如果我然后尝试从pickle
导入一个名称,它将从简单的pickle
模块中获取(我不会注意到它,因为它不会引发错误!)
>>> import cPickle as pickle
>>> from pickle import dump
>>> import inspect
>>> inspect.getsourcefile(dump)
'.../python2.7/pickle.py' # Expected cPickle.dump
糟糕!
我发现sys.modules
包含真实的模块名称(re
或cPickle
,但不包含别名regexp
或pickle
。这解释了< em>如何第二次导入失败,但为什么 python模块名称解析以这种方式工作,即这样做的规则和理由是什么。
注意:这被标记为a question的副本,与模块别名无关:问题中甚至没有提到别名(这是关于从包中导入子模块) )或最佳答案。虽然这个问题的答案提供了与这个问题相关的信息,但问题本身甚至都不相似恕我直言。
答案 0 :(得分:13)
您可以通过这种方式考虑加载过程:
您可以以变量的形式将模块加载到您的程序中。您可以为变量命名使用模块,无论您想要什么。 但 加载过程,是基于模块文件的名称,而不是“模块变量”。
import re
创建一个名为re
的全局变量,作为“模块门户”,提供了使用模块操作的能力。
最相似的是,import re as regex
在名为regex
的变量下创建了这样一个“门户”。
但是,在寻找创建此类门户并将模块功能加载到其中时,导入器不会使用此类引用。相反,它会在您的python \Lib
目录或当前工作目录中查找名为re.py
的文件(或者您导入的模块的名称) )。
import
说明不会解决变量,而是文件,例如C中的#include<stdio.h>
。它们具有“自己的语法”,并且由解释器结构规定的一组指令,对于这种情况,re
解释为文件名而不是变量,as
用于统治模块“门户”的名称。
这就是regex
是re
门户的操作别名的原因,但不 导入别名对于模块(为此目的,您必须使用文件的名称)。
我使用了“模块门户”和“操作别名”之类的术语,因为我没有找到任何标准术语。大多数模块和导入器机制都与解释器实现有关。例如,在CPython中(开发人员通常使用C API),create_module
使用提供的模块规范为导入器(以PyObject
s的形式)创建模块,并且带有模块属性的模块实例创建的PyModule_NewObject
和PyModule_New
函数。可以在C API modules decumentation。
当我提到术语“门户”作为引用import
语句创建的变量的方法时,我的意思是将其称为静态门户,而不是一个动态。模块文件中的更改不会反映在已导入它的正在运行的程序中(只要它没有重新加载它),因为它将加载模块的副本并使用它,而不是询问模块文件遇到需要时的操作。
以下是变量加载的实时性:
>>> import re
>>> re
<module 're' from 'C:\\Programs\\Python35\\lib\\re.py'>
>>> import re as regex
>>> regex
<module 're' from 'C:\\Programs\\Python35\\lib\\re.py'>
您可以看到re
模块引用,从加载文件C:\Programs\Python35\lib\re.py
(可能会根据您的位置而变化) python已安装)。
答案 1 :(得分:5)
您不能将import语句中的模块名称视为变量。如果是这种情况,那么您的初始导入肯定会失败,因为re
还不是声明的变量。基本上,import语句是语义糖;它是自己的规则声明。
一个这样的规则是:写入的模块名称被理解为它是一个字符串。也就是说,它不会查找名为re
的变量,而是直接使用字符串值're'
作为搜索模块名称。然后它搜索具有此名称的模块/包(文件)并执行导入。
这是唯一的情况(编辑:嗯,请参阅评论中的讨论...),在这种行为被看到的语言中,这是造成混淆的原因。考虑这种替代语法,它更符合Python语言的其余部分:
import 're'
# Or alternatively
module_name = 're'
import module_name
这里,在import语句中假设变量扩展。我们知道这是不实际为import语句选择的语法。人们可以讨论哪种语法更好,但上面的语言语法其余部分肯定更加和谐。
答案 2 :(得分:2)
要得到一个明确的答案,你必须问设计师自己,但我认为你问的是错误的问题。
问题不应该是:为什么这样做?“但它应该是,按照你要求的方式做什么会有什么好处?当然可以完了,但为什么要这样呢?
因为import
语句很简单且非常直观,你给它一个文件名,它试图找到加载它。你甚至可以看上as
和from
,但是,概念很简单,就是你编写文件名然后就可以了。
什么会混淆它并使其更难理解实现,唯一的成就就是让事情变得更加复杂。
Python有寻找其设计变更背后的基本原理的历史,人们问为什么不是function
对象的子类可以获得“为什么他们应该?”答复;这种行为实际上没有用例。因此,import
简单,直观,让人联想到在其他语言中包含/使用文件。
答案 3 :(得分:1)
使用import时,python会尝试查找from文件以导入您请求的内容。这可能会更清楚。
import re as regexp
from regexp import search
这基本上要求python查看名为&#39; regexp&#39;的文件。它无法找到。这就是别名不起作用的原因。