我想拥有一个大致如下所示的模块:
# my_module.py
my_number = 17
from other_module import foo
my_object = foo(23)
但是,存在一个问题:安装other_module
会给某些用户带来麻烦,并且仅对那些想使用my_object
的用户才需要-反过来,这又只是一小部分用户。我想让那些不需要my_object
的用户免于安装other_module
。
因此,我希望仅从other_module
导入my_object
时才进行my_module
的导入。换句话说,用户无需安装other_module
就可以运行以下程序:
from my_module import my_number
我可以通过包含导入的函数来提供my_object
:
# in my_module.py
def get_my_object():
from other_module import foo
my_object = foo(23)
return my_object
然后,用户必须执行以下操作:
from my_module import get_my_object
my_object = get_my_object()
是否有更好的方法来有条件地触发other_module
的导入?我最感兴趣的是让用户保持简单。
答案 0 :(得分:2)
我希望使用SliceConcatExt
方法,但是从Python 3.7开始,通过定义module-level __getattr__
function可以实现您所要求的:
get_my_object()
只有在访问# my_module.py
my_number = 17
def __getattr__(name):
if name != 'my_object':
raise AttributeError
global my_object
from other_module import foo
my_object = foo(23)
return my_object
后,这才会尝试导入other_module
并调用foo
。一些警告:
my_object
中的全局变量查找来尝试访问my_object
的尝试将不触发。只会在my_module
个属性访问或my_module.my_object
导入(在后台执行属性访问)时触发。from my_module import my_object
中的全局my_object
分配名称,则__getattr__
将在每次访问时重新计算。模块级my_object
在Python 3.7之前不执行任何操作,因此您可能需要执行版本检查并对Python 3.6及以下版本执行其他操作:
__getattr__
答案 1 :(得分:1)
创建一个新的单独模块,并让用户从另一个模块中导入对象。例如
from my_module import my_number # regular use
from my_module.extras import my_object # for a small part of the user base
这意味着在代码中,您将创建一个带有my_module
的模块文件夹__init__.py
,在其中您导入通常的内容,而不导入extras
子模块。
如果您不想将extras
放在my_module
中(简单),只需在单独的my_object
模块中创建extras.py
。
您可以使用importlib.import_module
在get_my_object
内动态导入模块而不会污染全局空间,并且比在函数内部进行导入更干净,该函数会产生诸如覆盖全局变量的副作用具有该导入名称(请参见this question and answers),但这通常 是代码其他部分编码模式错误的标志。
当某些用户可能没有库时,我通常倾向于这种简单的模式,因为Python 3会阻止不在顶层的导入:
try:
import other_module
_HAS_OTHER_MODULE_ = True
except:
_HAS_OTHER_MODULE_ = False
def get_my_object():
assert _HAS_OTHER_MODULE_, "Please install other module"
return other_module.foo(23)
答案 2 :(得分:1)
这是一个常见的技巧:
import sys
class MockModule:
def __init__(self, module):
self.module = module
def __getattr__(self, attr):
if attr == 'dependency_required_var':
try:
import foo
return self.module.dependency_required_var
except ImportError:
raise Exception('foo library is required to use dependency_required_var')
else:
return getattr(self.module, attr)
MockModule.__name__ = __name__
sys.modules[__name__] = MockModule(sys.modules[__name__])
dependency_required_var = 0
使用this PEP,我们可以在Python 3.7及更高版本中简单地执行以下操作(应该可以,但我无法使它工作):
def __getattr__(attr):
if attr == 'dependency_required_var':
try:
import foo
return dependency_required_var
except ImportError:
raise Exception('foo library is required to use dependency_required_var')
else:
return globals()[attr]
PEP似乎已被接受,但是来自PEP的相关拉取请求似乎已关闭,我实际上不确定它是否已实现。