Monkeypatching:将类替换为函数

时间:2014-07-23 14:25:29

标签: python monkeypatching

我有一些愚蠢的代码与猴子匹配部分。以下示例仅供自学而非生产。

class MyClass:

    def some_method(self):
        print("some_method call")
        self.yet_another_method()

    def yet_another_method(self):
        print('yet_another_method call')


def some_function(self):
    print("some function call")
    self.yet_another_method()

obj = MyClass()
obj.some_method()
obj.some_method = some_function
obj.some_method()

当我执行此代码时,出现以下错误:

TypeError: non_class_some_method() missing 1 required positional argument: 'self'

很明显,Python解释器无法隐式传递obj中的some_function。但是当我进行内省并获得字节代码时,我有类似的方法表示(替换之前和之后)。

import dis
import inspect


class MyClass:

    def some_method(self):
        print("some_method call")
        self.yet_another_method()

    def yet_another_method(self):
        print('yet_another_method call')


def some_function(self):
    print("some function call")
    self.yet_another_method()

obj = MyClass()
dis.dis(obj.some_method)
print(inspect.getargspec(obj.some_method))
obj.some_method = some_function
print("======================================================================")
dis.dis(obj.some_method)
print(inspect.getargspec(obj.some_method))

结果:

  8           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_CONST               1 ('some_method call') 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              

  9          10 LOAD_FAST                0 (self) 
             13 LOAD_ATTR                1 (yet_another_method) 
             16 CALL_FUNCTION            0 (0 positional, 0 keyword pair) 
             19 POP_TOP              
             20 LOAD_CONST               0 (None) 
             23 RETURN_VALUE         
ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)
======================================================================
 16           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_CONST               1 ('some function call') 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              

 17          10 LOAD_FAST                0 (self) 
             13 LOAD_ATTR                1 (yet_another_method) 
             16 CALL_FUNCTION            0 (0 positional, 0 keyword pair) 
             19 POP_TOP              
             20 LOAD_CONST               0 (None) 
             23 RETURN_VALUE         
ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)

请有人解释为什么会这样发生吗?

1 个答案:

答案 0 :(得分:3)

大部分答案都在这里:https://wiki.python.org/moin/FromFunctionToMethod。长话短说:

  • def 始终会产生function个对象
  • method只是函数,类和实例
  • 周围的一个瘦的可调用包装器
  • 只有当函数是类的属性时才会创建此包装器 - 而不是当它是实例的属性时。

要使代码工作(基于每个实例进行monkeypatching),您必须手动调用将从函数,类和实例创建method的机制。使用新式类时,最简单的解决方案是直接调用函数上的描述符协议,即:

obj.some_method = some_function.__get__(obj, type(obj))

当你在你的例子中使用旧式类时(这不是一个好主意),你可以使用types.MethodType代替,但除非你遇到一些遗留代码,否则你会更好使您的课程成为新式课程(=>继承自object)。