在 python 中执行模块主体之前,有没有办法在模块内部修补 class?

Is there a way to monkey patch a class inside a module before the module body is executed in python?

我有文件 main.py,其中包含以下代码:

class A:
  def __init__(self, a):
    self.a = a
  def run(self):
    return self.a+10

a = A(4)
print(a.run())

在文件 test.py 中,我尝试对 main.py 中的 class A 进行猴子补丁,如下所示:

import main

class A:
  def __init__(self, a):
    self.a = a
  def run(self):
    return self.a+5 

main.A = A

不幸的是,当我从 python 解释器 运行 import test 时,模块仍然打印出 14 而不是我预期的输出 9.

有没有办法在执行模块主体之前在模块内部修补 class?

这里的问题是,当您 import 编辑 main.py 文件时,它使用 class [=14= 的实际实现来执行代码 a = A(4) ].然后你的 test.py 的其余部分被执行并且你替换了 A 引用,但是为时已晚。 您可以通过添加测试来检查:

print(__name__)                     # __main__
print(main.__name__)                # so70731368_main
print(A.__module__)                 # __main__
print(main.A.__module__)            # __main__
print(main.a.__class__.__module__)  # so70731368_main

在这里,__main__ 有点令人困惑,但这就是 Python 调用您 运行 的第一个文件的方式(在您的情况下 test.py)。 a 实例是在 so70731368_main 模块中声明的,它使用了同一模块中的 A class,你只是在事后更改了 A来自测试文件的定义 (__main__).

您需要修补同一文件中定义的两个定义(Aa)这一事实非常棘手。 unittest.mock.patch 的功能不足以修补 inside 导入(它修补 after 导入)。
您不能以简洁明了的方式阻止 a 被实例化为 main.A(真实)class 并打印出来。你可以做的是修补它之后,供以后使用,这就是你所展示的。

直接回答您的问题:“修补”意味着用一个引用替换另一个引用,因此必须已经定义了引用。在您的示例中,它需要在 class 定义和 class 实例化之间进行修补(因为 print 不使用真正的 a),这是不支持的。
这个问题没有简单的解决方案。如果您可以控制 main.py 文件的代码,请尝试更改它,使其在导入时不实例化 a