簡體   English   中英

有條件地導入模塊以影子本地實現

[英]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)的第一次,你的myclassmodule_x有效地刪除並替換myclassmodule_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

您可以在此處閱讀有關python導入系統的更多信息

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM