【问题标题】:Why can't I import from a module alias?为什么我不能从模块别名导入?
【发布时间】:2025-12-03 10:05:02
【问题描述】:

我刚刚在 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,并且我想使用 C 版本的 picklecPickle。如果我尝试从pickle 导入名称,它将从简单的pickle 模块中获取(我不会注意到,因为它不会引发错误!)

>>> import cPickle as pickle
>>> from pickle import dump
>>> import inspect
>>> inspect.getsourcefile(dump)
'.../python2.7/pickle.py'    # Expected cPickle.dump 

哎呀!

我发现sys.modules 包含真正的模块名称(recPickle,但不包括别名regexppickle。这就解释了第二次导入的如何失败,但不是为什么 python 模块名称解析以这种方式工作,即这样做的规则和基本原理是什么。

注意:这被标记为与模块别名无关的a question 的重复:问题中甚至没有提到别名(这是关于从包中导入子模块)或最佳答案。尽管该问题的答案提供了与该问题相关的信息,但问题本身甚至与恕我直言。

【问题讨论】:

  • 因为导入机制 import Xfrom X import Y 不检查变量 X 的值,它会搜索名为“X”的文件/文件夹
  • 而不是from regexp import searchfrom pickle import dump,为什么不只是search = regexp.searchdump = pickle.dump
  • 因为在您的示例中,它没有按预期工作?您的“令人讨厌的惊喜”场景根本没有吸引力,因为有一个清晰而简单的解决方法。并且作为@jmd_dk pointed out,您不能使用相同的语法从静态模块名称和变量模块名称导入,因为它是模棱两可的:Python 什么时候应该逐字解释模块名称而不是动态解释模块名称? Python 的禅宗说:面对歧义,拒绝猜测的诱惑
  • 语法是import MODULE as NAME。答案是模块和名称不可互换。模块可以是文件名XXX.py,也可以是搜索路径中包含相应__init__.py 的文件。
  • 我理解该答案的相关性,但问题不应该与作为重复项关闭相关吗?

标签: python


【解决方案1】:

简而言之:

你可以这样想加载过程:

您可以以变量的形式将模块加载到您的程序中。您可以根据需要为使用模块的变量命名但是,加载过程是基于模块文件的名称,而不是“模块变量”。


长版:

import re 创建一个名为 re 的全局变量,作为“模块门户”,它提供了使用模块操作的能力。

最相似的是,import re as regex 在名为 regex 的变量下创建了这样一个“门户”。

但是,当希望创建此类门户并将模块功能加载到其中时,导入器使用此类引用。相反,它会在您的 python \Lib 目录或您当前的工作目录中查找模块,作为一个名为 re.py文件(或任何您导入的模块的名称)。

import 指令不处理变量,而是文件,就像C 中的#include&lt;stdio.h&gt;。它们有自己的“自己的语法”和一组指令,由解释器结构决定,在这种情况下,re 解释为文件名而不是变量,as 解释模块“门户”的名称。

这就是为什么regexre 门户的操作别名,但不是模块的导入别名 (为此,您必须使用文件的名称)。

  • 我使用了诸如“模块门户”和“操作别名”之类的术语,因为我没有找到任何标准术语。大多数模块和导入器机制都与解释器实现有关。例如,在 CPython(开发人员普遍使用 C API)中,create_module 使用提供的模块规范为导入器创建模块(以PyObjects 的形式),而PyModule_NewObjectPyModule_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 安装位置而改变) .

【讨论】:

  • 另外,你不能使用re.find,例如如果你使用import re as regexp,你必须使用regexp.find
  • 你能提供任何关于“模块门户”(或“触发器”)和“操作别名”概念的参考吗?我不记得在阅读 Python 原理时遇到过它们。
【解决方案2】:

您不能将导入语句中的模块名称视为变量。如果是这种情况,您的初始导入肯定会失败,因为re 还不是声明的变量。基本上导入语句是语义糖;它是一个有自己的规则的声明。

一个这样的规则是:写入的模块名称被理解为好像它是一个字符串。也就是说,它不查找名称为re 的变量,而是直接使用字符串值're' 作为寻找的模块名称。然后它会搜索具有此名称的模块/包(文件)并进行导入。

这是出现这种行为的语言中唯一的情况(编辑: 好吧,请参阅 cmets 中的讨论...),这是造成混乱的原因。考虑一下这种替代语法,它更符合 Python 语言的其余部分:

import 're'
# Or alternatively
module_name = 're'
import module_name

这里,变量扩展假设在 import 语句中。正如我们所知,这 不是 实际上为 import 语句选择的语法。大家可以讨论一下哪种语法更好,但上面的语法肯定是和其他语言语法更和谐的。

【讨论】:

  • 我认为这个答案比其他简单解决机制的答案更好地解释了 为什么 / 基本原理。
  • 关于您的更新:import 语句执行名称绑定,因此它需要一个有效的标识符,而不仅仅是任意字符串。支持诸如import &lt;string literal or variable&gt; 之类的假设语法将暗示比可能的更大的灵活性。它与其他涉及名称绑定的语法相协调,例如delglobalnonlocal。如果我们在事后提出建议,我会更喜欢引用绑定名称字符串的更通用的能力,例如Record = namedtuple('Record', &lt;...&gt;)uid = data['uid'] 等用例。
  • 我不同意delnonlocal 语句直接对原始标识符进行操作,这与import 的意义相同。在前两个的情况下,您提供一个现有对象作为参数(就像任何函数调用一样),并且完成了一些操作。在import 的情况下,您提供一个 映射到任何现有对象的标识符。在global 的情况下你是对的;标识符也不必映射到任何现有对象,这使得我之前的陈述“这是语言中唯一可以看到这种行为的情况”是错误的。我想我们现在有两种情况
  • 另外,正如您所指出的,在 import 语句中,标识符既用于文件名,又用作保存导入模块的变量名,将有效标识符限制为有效的标识符变量名,但这与参数无关。可以通过re_module = __import__('re') 导入来消除这种退化。这允许使用存储在文件名作为变量名无效的文件中的模块。
【解决方案3】:

要得到一个明确的答案,你必须问设计师自己,但是,我认为你问错了问题。

问题不应该是:为什么要这样做?”但是,应该是,按照你的要求这样做有什么好处?当然可以完成了,但为什么要这样做?

正如import 语句非常简单且非常直观,你给它一个文件名,它会尝试找到加载它。你甚至会喜欢asfrom,但是,这个概念很简单,你写文件名就可以了。

什么会使它变得模糊并使其更难理解,唯一的成就就是让事情变得更复杂。

Python 一直在寻找改变其设计背后的基本原理,人们问为什么 function 对象不能被子类化时会得到一个“他们为什么要这样做?”回复;这种行为并没有真正的用例。照原样,import 简单、直观,让人想起包含/使用其他语言的文件。

【讨论】:

    【解决方案4】:

    当使用 from import 时,python 会尝试在 from 文件中查找以导入您请求的内容。这可能会更清楚。

    import re as regexp
    
    from regexp import search 
    

    这实质上是要求 python 查找一个它找不到的名为 'regexp' 的文件。这就是别名不起作用的原因。

    【讨论】: