[英]conditionally import module to shadow local implementation
I am writing a Python script where some of the core functionalities can be done by another existing library. 我正在编写一个Python脚本,其中某些核心功能可以由另一个现有库完成。 Unfortunately, while that library has more features, it is also slower, so I'd like if the user could at runtime select whether they want to use that library or my own fast and simple implementation.
不幸的是,尽管该库具有更多功能,但它也较慢,所以我想让用户在运行时可以选择是否要使用该库还是我自己的快速简单的实现。 Unfortunately I'm stuck at a point where I don't understand some of the workings of Python's module system.
不幸的是,我陷入了一个无法理解Python模块系统某些功能的地步。
Suppose that my main program was main.py
, that the (optional) external module is in module_a.py
and that my own fast and simple implementation of module_a
together with the actual program code that uses either my own implementation or the one of module_a
is in the file module_x.py
: 假设我的主程序是
main.py
,(可选的)外部模块在module_a.py
,并且我自己对module_a
的快速简单实现以及使用我自己的实现或module_a
的一个的实际程序代码是在module_x.py
文件中:
main.py: main.py:
import module_x
module_x.test(True)
module_x.test(False)
module_a.py: module_a.py:
class myclass():
def __init__(self):
print("i'm myclass in module_a")
module_x.py: 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()
When I now execute main.py
I get: 现在执行
main.py
我得到:
$ python3 main.py
i'm myclass in module_a
i'm myclass in module_a
But why is this? 但是为什么呢? If
False
is passed to test()
then the import of the module_a
implementation should never happen. 如果将
False
传递给test()
则绝不应该导入module_a
实现。 Instead it should only see myclass
from the local file. 相反,它应该只从本地文件中看到
myclass
。 Why doesn't it? 为什么不呢? How do I make
test()
use the local definition of myclass
conditionally? 如何使
test()
有条件地使用myclass
的本地定义?
My solution is supposed to run in Python3 but I see the same effect when I use Python2.7. 我的解决方案应该在Python3中运行,但是当我使用Python2.7时会看到相同的效果。
An import
statement is permanent within the thread of execution unless it is explicitly undone. 除非明确撤消,否则
import
语句在执行线程中是永久的。 Furthermore, once the from ... import
statement is executed in this case, it replaces the variable myclass
in the global scope (at which point the class it was previously referencing defined in the same file is no longer referenced and can in theory be garbage collected) 此外,在这种情况下执行了
from ... import
语句后,它将替换全局范围内的变量myclass
(这时不再引用以前在同一文件中定义的类,因此从理论上讲可以是垃圾集)
So what is happening here is whenever you run test(True)
the first time, your myclass
in module_x
is effectively deleted and replaced with the myclass
from module_a
. 那么,什么是这里发生的事情是当你运行
test(True)
的第一次,你的myclass
在module_x
有效地删除并替换myclass
从module_a
。 All subsequent calls to test(False)
then call global myclass
which is effectively a no-op since the global myclass now refers to the one imported from the other class (and besides the global
call is unneeded when not changing the global variable from a local scope as explained here ). 随后所有对
test(False)
调用都将调用global myclass
,这实际上是一个无操作操作,因为全局myclass现在引用的是从另一个类导入的一个(此外,当不从本地更改全局变量时,不需要global
调用)范围,因为解释这里 )。
To work around this, I would strongly suggest encapsulating the desired module-switching behavior in a class that is independent of either module you would like to switch. 要解决此问题,我强烈建议将所需的模块切换行为封装在一个类中,该类与您要切换的任一模块无关。 You can then charge that class with holding a reference to both modules and providing the rest of you client code with the correct one.
然后,您可以对该类进行收费,方法是保留对这两个模块的引用,并为其余的客户代码提供正确的代码。 Eg
例如
module_a_wrapper.py 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: main.py:
from module_a_wrapper import set_module, get_module
set_module(True)
get_module().myclass()
set_module(False)
get_module().myclass()
Running: 运行:
python main.py
# Outputs:
i'm myclass in module_a
i'm myclass in module_x
You can read more about the guts of the python import system here 您可以在此处阅读有关python导入系统的更多信息
The answer by lemonhead properly explains why this effect happens and gives a valid solution. Lemonhead的答案正确地解释了为什么会发生这种影响并给出了有效的解决方案。
The general rule seems to be: wherever and however you import a module, it will always replace any variables of the same name from the global scope. 一般规则似乎是:无论在何处导入模块,它都将始终替换全局作用域中同名的任何变量。
Funnily, when I use the import foo as bar
construct, then there must neither be a global variable named foo
nor one named bar
! 有趣的是,当我使用
import foo as bar
构造时,既不能有一个名为foo
的全局变量,也不能有一个名为bar
的全局变量!
So while lemonhead's solution worked it adds lots of complexity and will lead to my code being much longer because every time I want to get something from either module I have to prefix that call with the getter function. 因此,尽管Lemonhead的解决方案有效,但它增加了很多复杂性,并且会导致我的代码更长,因为每次我想从这两个模块中获取任何东西时,都必须在该调用前加上getter函数。
This solution allows me to solve the problem with a minimal amount of changed code: 此解决方案使我可以用最少的更改代码来解决问题:
module_x.py: 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()
So the only thing I changed was to rename the class I had in global scope from myclass
to myclass_fast
. 因此,我唯一要做的就是将全局范围内的类从
myclass
重命名为myclass_fast
。 This way it will not be overwritten anymore by the import of myclass
from module_a
. 这样,将不再被从
module_a
导入myclass
module_a
。 Then, on demand, I change the local variable myclass
to either be the imported module or myclass_fast
. 然后,根据需要,将局部变量
myclass
更改为导入的模块或myclass_fast
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.