我想monkey patch外部代码库来检查同一功能的替代版本。
目前项目树是这样的
.
├── mymodule
│ ├── __init__.py
│ └── myA.py
└── subProjectsDir
└── subProject
└── subonemodule
├── __init__.py
└── subtwomodule
├── __init__.py
├── subA.py
└── subthreemodule
├── __init__.py
└── subB.py
为了更好地在您的计算机上重现该示例,我将项目以 zip 文件形式上传到我的个人云存储空间(此处)。 不过,我会将每个文件的内容保留在这里。
让我们从我需要修复的子项目开始。我把它放在
subProjectsDir/subProject
目录中。
subonemodule/__init__.py
from . import subtwomodule
subonemodule/subtwomodule/__init__.py
from .subA import funA
subonemodule/subtwomodule/subA.py
def funA():
print("submoudle subA -> fun A")
这就是我要猴子补丁的功能
subonemodule/subtwomodule/subthreemodule/__init__.py
from .subB import funB
subonemodule/subtwomodule/subthreemodule/subB.py
from ...subtwomodule.subA import funA
def funB():
print("subsubmoudle subB -> fun B")
funA()
请注意,这是我的模块调用的函数。此函数依次调用我需要修补的函数。然而,进口链是相对的,这可能会导致问题。
mymodule/__init__.py
import os
import sys
thisdir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(thisdir, "..", "subProjectsDir", "subProject"))
print(f"From mymodule launch: {sys.path}")
之前的cose是用来使用我代码里面的子项目的。
mymodule/myA.py
from subonemodule import subtwomodule
from subonemodule.subtwomodule import subA
from subonemodule.subtwomodule.subthreemodule.subB import funB
def myFunA():
print("My A")
def main():
# Monkey patch 1, not working
# subA.funA = myFunA
# Monkey patch 2, not working
subtwomodule.subA.funA = myFunA
funB()
if __name__ == "__main__":
main()
该文件包含我对函数进行猴子修补的两次尝试
funA
。更清楚地说,目标是每当从属于子项目的任何模块调用函数 funA
时,此类调用都会被对我的函数 myFunA
的调用所取代。
然而,这两种方法都不起作用。
$> python3 -m mymodule.myA
sub3module subB -> fun B
sub2module subA -> fun A
这种情况我该怎么办?我试图不碰所有外部子项目。
您需要先修补该函数,然后再由您或您希望覆盖可见的任何其他模块导入该函数。
from subonemodule import subtwomodule
def myFunA():
print("My A")
# patch funA before any other imports
subtwomodule.subA.funA = myFunA
from subonemodule.subtwomodule.subthreemodule.subB import funB
def main():
funB()
if __name__ == "__main__":
main()
打印函数地址可能有助于理解发生了什么。
这不起作用
from subonemodule import subtwomodule
from subonemodule.subtwomodule.subthreemodule.subB import funB
def myFunA(): print("My A")
print("original funA", id(subtwomodule.subA.funA))
print("patched funA", id(myFunA))
# patch funA
subtwomodule.subA.funA = myFunA
print("subB's funA", id(subtwomodule.subthreemodule.subB.funA))
funB()
输出:
original funA 4339032416
patched funA 4338922368
subB's funA 4339032416
sub3module subB -> fun B
sub2module subA -> fun A
sub2module
在 funA
修补之前导入,因此它导入了原始 funA
from subonemodule import subtwomodule
def myFunA():print("My A")
print("original funA", id(subtwomodule.subA.funA))
print("patched funA", id(myFunA))
# patch before importing
subtwomodule.subA.funA = myFunA
from subonemodule.subtwomodule.subthreemodule.subB import funB
print("subB's funA", id(subtwomodule.subthreemodule.subB.funA))
funB()
输出:
original funA 4330299744
patched funA 4330189696
subB's funA 4330189696
sub3module subB -> fun B
My A
funA
在被 subB
导入之前已被修补,因此当导入 subB 时,它只能看到修补的 funA
。