【问题标题】:Monkeypatching: replacing method on class to functionMonkeypatching:将类上的方法替换为功能
【发布时间】:2014-07-23 14:25:28
【问题描述】:

我有一些带有猴子匹配部分的愚蠢代码。以下示例仅供自学使用,不用于生产。

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解释器不能在some_function中隐式传递obj。但是当我进行自省并获取字节码时,我有类似的方法表示(替换之前和之后)。

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)

请谁能解释一下为什么会这样?

【问题讨论】:

  • 发生这种情况是因为在实例上定义的函数永远不会转换为绑定方法,它们应该定义为类属性。(或者您可以使用types.MethodType 将函数注册为使用实例的方法。)
  • 如果您只想更改类的一个实例的方法,而不是类的所有实例,那么您的问题在这里得到解答:stackoverflow.com/questions/972/…

标签: python monkeypatching


【解决方案1】:

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

  • def 总是产生一个 function 对象
  • method 只是函数、类和实例的薄可调用包装器
  • 仅当函数是类的属性时才创建此包装器,而不是实例的属性。

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

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

当使用像您在示例中那样的旧式类时(这不是一个好主意),您可以改用 types.MethodType,但实际上,除非您遇到一些遗留代码,否则您最好制作您的类一个新式的(=> 继承自 object)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 2013-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多