【问题标题】:Monkey patching multiple class members in a loop in python猴子在python中循环修补多个类成员
【发布时间】:2022-01-15 13:27:20
【问题描述】:

我想在一个循环中一次修补一个类的多个方法,以修改一个类如何在所有模块中进行测试。

我认为来自 javascript 的 object.assign 会做类似的事情。

这行得通:

class A:
  def foo1(self):
    print("a")
  def foo2(self):
    print("a")

class B:
  def foo1(self):
    print("b")
  def foo2(self):
    print("b")

A.foo1 = B.foo1
A.foo2 = B.foo2
A().foo1()  # prints "b"
A().foo2()  # prints "b"

我想在循环中执行此操作以避免必须单独指定每个成员。

【问题讨论】:

    标签: python python-unittest


    【解决方案1】:

    下面的程序会过滤类的属性和方法,并且会排除私有方法和所有属性的替换。

    import collections
    from types import FunctionType
    
    class A:
      def foo1(self):
        print("a")
      def foo2(self):
        print("a")
    
    class B:
      a = 1
      def foo1(self):
        print("b")
      def foo2(self):
        print("b")
    
    
    def patch_class(original, replacement):
        for key, val in replacement.__dict__.items():
            if key.startswith("_") or not isinstance(val, FunctionType):
                continue
            setattr(original, key, val)
    
    
    patch_class(A, B)
    A().foo1()  # prints "b"
    A().foo2()  # prints "b"
    
    

    【讨论】:

      【解决方案2】:

      vars 返回所有方法,setattrgetattr 可以根据它们的字符串名称进行读写。

      __dict__这样的某些成员不能被替换,所以它被包裹在try/except中。

      def patch_class(original, replacement):
          """Replaces all methods in original with the ones in replacement."""
          for member in vars(replacement).keys():
              try:
                  target = getattr(replacement, member)
                  setattr(original, member, target)
                  # or:
                  # patch.object(original, member, target).__enter__()
                  # type.__setattr__(original, member, target)
                  print("monkey-patched", member)
              except Exception as e:
                  print(f"could not monkey-patch {member}: {e.__class__.__name__} {e}")
      
      class A:
        def foo1(self):
          print("a")
        def foo2(self):
          print("a")
      
      class B:
        def foo1(self):
          print("b")
        def foo2(self):
          print("b")
      
      patch_class(A, B)
      
      A().foo1()  # prints "b"
      A().foo2()  # prints "b"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-30
        • 1970-01-01
        • 2013-11-01
        • 2020-06-18
        • 2011-04-15
        • 2014-03-05
        • 2011-04-25
        • 1970-01-01
        相关资源
        最近更新 更多