[英]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.