首页 文章

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

提问于
浏览
91

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

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

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

5 回答

  • -1

    更改导入路径:

    import sys
    save_path = sys.path[:]
    sys.path.remove('')
    import calendar
    sys.path = save_path
    
  • 34

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

    from __future__ import absolute_import
    import socket
    

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

  • 1

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

    重命名您的模块 .

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

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

    如果您采取危险的路径,您将遇到的一个问题是,当您加载模块时,它最终会得到一个'official name',这样Python就可以避免再次解析该模块的内容 . 可以在 sys.modules 中找到模块的'official name'到模块对象本身的映射 .

    这意味着,如果您在一个地方 import calendar ,那么导入的任何模块都将被视为具有官方名称 calendar 的模块以及其他任何地方的所有其他尝试 import calendar ,包括在其他代码中属于主Python库的模块,将获得该模块日历 .

    有可能使用Python 2.x中的imputil module来设计客户导入器,导致从某些路径加载的模块首先查找他们导入的模块,而不是首先查找 sys.modules 之类的东西 . 但无论如何,_330582_在Python 3.x中工作 .

    你可以做一件非常丑陋和可怕的事情,不涉及钩住进口机制 . 这是你可能不应该做的事情,但它可能会奏效 . 它将您的 calendar 模块转换为系统日历模块和日历模块的混合体 . 感谢Boaz Yanivskeleton of the function I use . 把它放在 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__])
    
  • 123

    我想提供我的版本,这是Boaz Yaniv和Omnifarious的解决方案的组合 . 它将导入模块的系统版本,与以前的答案有两个主要区别:

    • 支持'dot'表示法,例如 . package.module

    • 是系统模块上import语句的替代品,这意味着您只需要替换那一行,如果已经对模块进行了调用,它们将按原样运行

    把它放在可以访问的地方,这样你就可以调用它(我的__init__.py文件中有我的):

    class SysModule(object):
        pass
    
    def import_non_local(name, local_module=None, path=None, full_name=None, accessor=SysModule()):
        import imp, sys, os
    
        path = path or sys.path[1:]
        if isinstance(path, basestring):
            path = [path]
    
        if '.' in name:
            package_name = name.split('.')[0]
            f, pathname, desc = imp.find_module(package_name, path)
            if pathname not in __path__:
                __path__.insert(0, pathname)
            imp.load_module(package_name, f, pathname, desc)
            v = import_non_local('.'.join(name.split('.')[1:]), None, pathname, name, SysModule())
            setattr(accessor, package_name, v)
            if local_module:
                for key in accessor.__dict__.keys():
                    setattr(local_module, key, getattr(accessor, key))
            return accessor
        try:
            f, pathname, desc = imp.find_module(name, path)
            if pathname not in __path__:
                __path__.insert(0, pathname)
            module = imp.load_module(name, f, pathname, desc)
            setattr(accessor, name, module)
            if local_module:
                for key in accessor.__dict__.keys():
                    setattr(local_module, key, getattr(accessor, key))
                return module
            return accessor
        finally:
            try:
                if f:
                    f.close()
            except:
                pass
    

    示例

    我想导入mysql.connection,但我有一个本地包已经叫mysql(官方的mysql实用程序) . 所以为了从系统mysql包中获取连接器,我将其替换为:

    import mysql.connector
    

    有了这个:

    import sys
    from mysql.utilities import import_non_local         # where I put the above function (mysql/utilities/__init__.py)
    import_non_local('mysql.connector', sys.modules[__name__])
    

    结果

    # This unmodified line further down in the file now works just fine because mysql.connector has actually become part of the namespace
    self.db_conn = mysql.connector.connect(**parameters)
    
  • 13

    实际上,解决这个问题相当容易,但实现总是有点脆弱,因为它取决于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
    

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

相关问题