问题:存在相同名称的模块时从内置库导入
情况:-我的project_folder中有一个名为Calendar的模块-我想使用Python库中的内置Calendar类-当我从日历导入Calendar中使用时,它抱怨,因为它试图从我的模块中加载。
我进行了几次搜索,但似乎找不到解决我问题的方法。
- 当存在具有相同名称的本地模块时,如何在Python中访问标准库模块?
- http://docs.python.org/whatsnew/2.5.html
- 在python中导入模块时,如何避免一直写模块名称?
有任何想法而不必重命名我的模块吗?
回答 0
公认的解决方案包含一种现已弃用的方法。
这里的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)
因此,您可以从路径加载任何.py文件,并将模块名称设置为所需的名称。所以只要调整module_name
为您希望模块在导入时使用的任何自定义名称即可。
要加载程序包而不是单个文件,file_path
应为程序包根目录的路径__init__.py
回答 1
无需更改模块名称。相反,您可以使用absolute_import更改导入行为。例如,以stem / socket.py导入套接字模块,如下所示:
from __future__ import absolute_import
import socket
这仅适用于Python 2.5及更高版本;这是Python 3.0及更高版本中的默认行为。Pylint会抱怨该代码,但这是完全有效的。
回答 2
实际上,解决这个问题很容易,但是实现总是有些脆弱,因为它取决于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
如果可能的话,最好的解决方案是避免使用与标准库或内置模块名称相同的名称来命名模块。
回答 3
解决此问题的唯一方法是自己劫持内部进口设备。这并不容易,而且充满了危险。您应不惜一切代价避免使用圣杯形的信标,因为其危险性很高。
重命名您的模块。
如果您想学习如何劫持内部导入机制,可以在这里找到如何执行此操作的方法:
有时有充分的理由陷入这种危险。您给出的原因不在其中。重命名您的模块。
如果您选择危险的路径,将会遇到的一个问题是,当您加载模块时,它会以“正式名称”结尾,这样Python就可以避免再次解析该模块的内容。可以在中找到模块的“正式名称”到模块对象本身的映射sys.modules
。
这意味着,如果您 import calendar
在某个地方,那么导入的任何模块都将被视为具有官方名称的模块,calendar
并且所有其他尝试到import calendar
任何其他地方的模块(包括在主Python库的其他代码中)都将获得该日历。
可能可以使用 Python 2.x中 imputil模块模块导致从某些路径加载的模块以不同于sys.modules
first或类似的方式查找要导入的模块。但这是一件非常繁琐的事情,而且无论如何在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__])
回答 4
我想提供我的版本,该版本是Boaz Yaniv和Omnifarious解决方案的结合。它将导入模块的系统版本,与先前的答案有两个主要区别:
- 支持“点”表示法,例如。包模块
- 是系统模块上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)
回答 5
更改导入路径:
import sys
save_path = sys.path[:]
sys.path.remove('')
import calendar
sys.path = save_path