如何在项目中有同名模块时从标准库导入?(如何控制Python查找模块的位置?)
在我的项目文件夹里,有一个叫做 calendar
的模块。在代码的其他地方,我想使用标准库里的 Calendar
类。但是当我尝试用 from calendar import Calendar
来导入这个类时,它却从我自己的模块导入了,导致后面出现错误。
我该怎么避免这个问题呢?我需要重命名这个模块吗?
6 个回答
39
其实,解决这个问题挺简单的,但实现起来总会有点脆弱,因为它依赖于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
如果可能的话,最好的解决办法是避免把你的模块命名为标准库或内置模块的名字。
146
其实不需要重命名这个模块。从Python 2.5版本开始,你可以使用 absolute_import
来改变导入模块的方式。
比如说,如果你想导入标准库里的 socket
模块,即使你的项目里有一个叫 socket.py
的文件,也可以这样做:
from __future__ import absolute_import
import socket
在Python 3.x中,这种行为是默认的。虽然Pylint会对这段代码提出警告,但其实这段代码是完全有效的。
8
在Python 3.5及以上版本中,可以使用标准库中的importlib
模块,直接从指定的路径导入文件,这样就不需要通过import
的查找机制了:
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)
在实际代码中,file_path
可以设置为任何一个.py
文件的路径,以便导入;module_name
应该是要导入的模块的名称(这是导入系统在后续import
语句中查找模块时使用的名称)。后面的代码会用module
作为模块的名称;如果想用不同的名称,可以把变量名module
改成其他名字。
如果想加载一个包而不是单个文件,file_path
应该指向包的根目录下的__init__.py
文件。