当存在相同名称的模块时,从内置库导入
情况: – 我的project_folder中有一个叫做calendar的模块 – 我想从Python库中使用内置的Calendar类 – 当我从日历中使用import日历时,它抱怨,因为它试图从我的模块加载。
我做了一些search,我似乎无法find解决我的问题。
- 如果有一个名称相同的本地模块,如何访问Python中的标准库模块?
- http://docs.python.org/whatsnew/2.5.html
- 如何避免在python中导入模块时一直写模块的名字?
任何想法,而不必重新命名我的模块?
实际上,解决这个问题是相当容易的,但是实现总是有些脆弱,因为它依赖于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
如果可能,最好的解决scheme是避免将模块命名为标准库或内置模块名称。
改变你的模块的名字是没有必要的。 相反,您可以使用absolute_import来更改导入行为。 例如,使用stem / socket.py导入套接字模块,如下所示:
from __future__ import absolute_import import socket
这只适用于Python 2.5及以上版本; 它启用了Python 3.0和更高版本中的默认行为。 Pylint会抱怨代码,但它是完全有效的。
解决这个问题的唯一方法是自己劫持内部的import机器。 这并不容易,充满了危险。 你应该不惜一切代价避免圣杯形灯塔,因为危险太危险了。
重命名你的模块。
如果你想学习如何劫持内部的import机器,这里是你要去find如何做到这一点:
- Python 2.7文档的导入模块部分
- Python 3.2文档的导入模块部分
- PEP 302 – 新的import钩子
有时有很好的理由进入这个危险。 你给的理由不在其中。 重命名你的模块。
如果你采取危险的道路,你会遇到的一个问题是,当你加载一个模块时,它会以“官方名称”结尾,这样Python就可以避免再次parsing该模块的内容。 sys.modules
可以find模块“官方名称”与模块对象本身的映射。
这意味着,如果您在一个地方import calendar
,那么import calendar
任何模块都将被视为具有正式名称calendar
的模块,并且所有其他尝试import calendar
其他任何地方的import calendar
,包括作为主要Python库一部分的其他代码,都将得到那个日历。
使用Python 2.x中的imputil模块devise一个客户导入程序是可能的,这使得从某些path加载的模块可以首先查找他们导入的模块,而不是sys.modules
或类似的东西。 但是这是一件非常有趣的事情,无论如何,它在Python 3.x中将不起作用。
有一个非常丑陋和可怕的事情,你可以做,不涉及挂钩的import机制。 这是你可能不应该做的事,但它可能会工作。 它将您的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__])
我想提供我的版本,这是Boaz Yaniv和Omnifarious的解决scheme的组合。 它将导入一个模块的系统版本,与以前的答案有两个主要区别:
- 支持“点”符号,例如。 package.module
- 是系统模块上的导入语句的替代,意味着您只需要replace那一行,并且如果已经对模块进行了调用,则它们将按原样工作
把这个地方放在一个可访问的地方,这样你就可以调用它(我的__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包中获取连接器,我将其replace为:
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)
更改导入path:
import sys save_path = sys.path[:] sys.path.remove('') import calendar sys.path = save_path