当存在具有相同名称的模块时从内置库导入

twi*_*wig 108 python import

情况: - 我的project_folder中有一个名为calendar的模块 - 我想使用Python库中的内置Calendar类 - 当我使用日历导入日历时,它会抱怨因为它试图从我的模块加载.

我做了一些搜索,似乎无法找到问题的解决方案.

任何想法,而无需重命名我的模块?

Dam*_*ian 135

无需更改模块的名称.相反,您可以使用absolute_import更改导入行为.例如,使用stem/socket.py,我按如下方式导入套接字模块:

from __future__ import absolute_import
import socket
Run Code Online (Sandbox Code Playgroud)

这仅适用于Python 2.5及更高版本; 它的启用行为是Python 3.0及更高版本中的默认行为.Pylint会抱怨代码,但它完全有效.

  • 这是解决方案.我检查了Python 2.7.6,这是必需的,它仍然不是默认值. (5认同)
  • 这似乎是对我的正确答案.参见[2.5 changelog](http://docs.python.org/release/2.5/whatsnew/pep-328.html)或[PEP328](http://www.python.org/dev/peps/pep- 0328 /#rationale-for-absolute-imports)了解更多信息. (4认同)
  • 这是正确的解决方案.不幸的是,当启动包中的代码时它不起作用,因为那时包不被识别,并且本地路径被添加到'PYTHONPATH`之前.[另一个问题](http://stackoverflow.com/q/12011327/1968)展示了如何解决这个问题. (4认同)
  • 确实:根据https://docs.python.org/2/library/__future__.html,这个行为默认的第一个python版本是3.0. (3认同)

Boa*_*niv 36

实际上,解决这个问题相当容易,但实现总是有点脆弱,因为它取决于python导入机制的内部结构,并且它们在未来的版本中会发生变化.

(以下代码显示了如何加载本地和非本地模块以及它们如何共存)

def import_non_local(name, custom_name=None):
    import imp, sys

    custom_name = custom_name or name

    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(custom_name, f, pathname, desc)
    f.close()

    return module

# Import non-local module, use a custom name to differentiate it from local
# This name is only used internally for identifying the module. We decide
# the name in the local scope by assigning it to the variable calendar.
calendar = import_non_local('calendar','std_calendar')

# import local module normally, as calendar_local
import calendar as calendar_local

print calendar.Calendar
print calendar_local
Run Code Online (Sandbox Code Playgroud)

如果可能,最佳解决方案是避免使用与标准库或内置模块名称相同的名称命名模块.

  • 谢谢波阿斯!虽然你的代码片段更短(和文档),但我认为重命名模块比使用一些可能会让人(或我自己)将来困惑的hacky代码更容易. (2认同)

Omn*_*ous 14

解决这个问题的唯一方法就是自己劫持内部进口机器.这并不容易,而且充满了危险.你应该不惜一切代价避免使用圣杯形状的灯塔,因为危险太危险了.

重命名您的模块.

如果你想学习如何劫持内部导入机器,你可以在这里找到如何做到这一点:

有时候很有理由陷入这种危险之中.你给出的理由不在其中.重命名您的模块.

如果你采取危险的路径,你将遇到的一个问题是,当你加载一个模块时,它最终会有一个"官方名称",这样Python就可以避免再次解析该模块的内容.可以在中找到模块的"正式名称"到模块对象本身的映射sys.modules.

这意味着,如果您import calendar在一个地方,导入的任何模块将被视为具有正式名称的模块,calendar并且所有其他尝试到import calendar其他任何地方,包括在主要Python库的其他代码中,将获得该日历.

也许可以使用Python 2.x中的imputil模块设计一个客户导入,它导致从某些路径加载的模块以非sys.modules首先或类似的方式查找他们导入的模块.但这是一个非常毛茸茸的事情,无论如何它都无法在Python 3.x中运行.

你可以做一件非常丑陋和可怕的事情,不涉及钩住进口机制.这是你可能不应该做的事情,但它可能会奏效.它将您的calendar模块转换为系统日历模块和日历模块的混合体.感谢Boaz Yaniv我使用的功能骨架.把它放在你calendar.py文件的开头:

import sys

def copy_in_standard_module_symbols(name, local_module):
    import imp

    for i in range(0, 100):
        random_name = 'random_name_%d' % (i,)
        if random_name not in sys.modules:
            break
        else:
            random_name = None
    if random_name is None:
        raise RuntimeError("Couldn't manufacture an unused module name.")
    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(random_name, f, pathname, desc)
    f.close()
    del sys.modules[random_name]
    for key in module.__dict__:
        if not hasattr(local_module, key):
            setattr(local_module, key, getattr(module, key))

copy_in_standard_module_symbols('calendar', sys.modules[copy_in_standard_module_symbols.__module__])
Run Code Online (Sandbox Code Playgroud)


Bra*_*ato 5

已接受的解决方案包含一种现已弃用的方法。

此处的 importlib 文档提供了一个很好的示例,说明了直接从 python >= 3.5 的文件路径加载模块的更合适的方法:

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__  # returns "/path/to/tokenize.py"
module_name = tokenize.__name__  # returns "tokenize"

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
Run Code Online (Sandbox Code Playgroud)

因此,您可以从路径加载任何 .py 文件并将模块名称设置为您想要的任何名称。因此,只需将 调整为module_name您希望模块在导入时具有的任何自定义名称。

要加载包而不是单个文件,file_path应该是包根目录的路径__init__.py

  • 默认的 python 模块位于哪里?你怎么知道它在每个系统上都是一样的?该解决方案可移植吗? (2认同)
  • 第一句应该删除......否则会让人们认为该解决方案包含现已弃用的方法。 (2认同)

归档时间:

查看次数:

47360 次

最近记录:

8 年,8 月 前