[英]conditionally import module to shadow local implementation
我正在编写一个Python脚本,其中某些核心功能可以由另一个现有库完成。 不幸的是,尽管该库具有更多功能,但它也较慢,所以我想让用户在运行时可以选择是否要使用该库还是我自己的快速简单的实现。 不幸的是,我陷入了一个无法理解Python模块系统某些功能的地步。
假设我的主程序是main.py
,(可选的)外部模块在module_a.py
,并且我自己对module_a
的快速简单实现以及使用我自己的实现或module_a
的一个的实际程序代码是在module_x.py
文件中:
main.py:
import module_x
module_x.test(True)
module_x.test(False)
module_a.py:
class myclass():
def __init__(self):
print("i'm myclass in module_a")
module_x.py:
class myclass():
def __init__(self):
print("i'm myclass in module_x")
def test(enable_a):
if enable_a:
try:
from module_a import myclass
except ImportError:
global myclass
enable_a = False
else:
global myclass
i = myclass()
现在执行main.py
我得到:
$ python3 main.py
i'm myclass in module_a
i'm myclass in module_a
但是为什么呢? 如果将False
传递给test()
则绝不应该导入module_a
实现。 相反,它应该只从本地文件中看到myclass
。 为什么不呢? 如何使test()
有条件地使用myclass
的本地定义?
我的解决方案应该在Python3中运行,但是当我使用Python2.7时会看到相同的效果。
除非明确撤消,否则import
语句在执行线程中是永久的。 此外,在这种情况下执行了from ... import
语句后,它将替换全局范围内的变量myclass
(这时不再引用以前在同一文件中定义的类,因此从理论上讲可以是垃圾集)
那么,什么是这里发生的事情是当你运行test(True)
的第一次,你的myclass
在module_x
有效地删除并替换myclass
从module_a
。 随后所有对test(False)
调用都将调用global myclass
,这实际上是一个无操作操作,因为全局myclass现在引用的是从另一个类导入的一个(此外,当不从本地更改全局变量时,不需要global
调用)范围,因为解释这里 )。
要解决此问题,我强烈建议将所需的模块切换行为封装在一个类中,该类与您要切换的任一模块无关。 然后,您可以对该类进行收费,方法是保留对这两个模块的引用,并为其余的客户代码提供正确的代码。 例如
module_a_wrapper.py
import module_x
import module_a
class ModuleAWrapper(object):
_target_module = module_x # the default
@classmethod
def get_module(cls):
return cls._target_module
def set_module(enable_a):
if enable_a:
ModuleAWrapper._target_module = module_a
else:
ModuleAWrapper._target_module = module_x
def get_module():
return ModuleAWrapper.get_module()
main.py:
from module_a_wrapper import set_module, get_module
set_module(True)
get_module().myclass()
set_module(False)
get_module().myclass()
运行:
python main.py
# Outputs:
i'm myclass in module_a
i'm myclass in module_x
Lemonhead的答案正确地解释了为什么会发生这种影响并给出了有效的解决方案。
一般规则似乎是:无论在何处导入模块,它都将始终替换全局作用域中同名的任何变量。
有趣的是,当我使用import foo as bar
构造时,既不能有一个名为foo
的全局变量,也不能有一个名为bar
的全局变量!
因此,尽管Lemonhead的解决方案有效,但它增加了很多复杂性,并且会导致我的代码更长,因为每次我想从这两个模块中获取任何东西时,都必须在该调用前加上getter函数。
此解决方案使我可以用最少的更改代码来解决问题:
module_x.py:
class myclass_fast():
def __init__(self):
print("i'm myclass in module_x")
def test(enable_a):
if enable_a:
try:
from module_a import myclass
except ImportError:
enable_a = False
myclass = myclass_fast
else:
myclass = myclass_fast
i = myclass()
因此,我唯一要做的就是将全局范围内的类从myclass
重命名为myclass_fast
。 这样,将不再被从module_a
导入myclass
module_a
。 然后,根据需要,将局部变量myclass
更改为导入的模块或myclass_fast
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.