【问题标题】:How to extend Class instance如何扩展类实例
【发布时间】:2017-01-27 22:33:57
【问题描述】:

MyClassmodule.py 中定义。我们无法修改它。但我们确实知道 Class 定义如下所示:

class MyClass:
    def method(self, msg):
        print 'from method:', msg

我从导入模块开始我的脚本,然后声明一个对象的实例:

import module    
foo = module.MyClass()

那我自己写函数:

def function(msg):
    print 'from function:', msg

现在,每次使用 foo.method('') 时,我都想调用 function(),所以它也会打印相同的消息。

这种情况会被称为monkey patching吗?如何实现所需要的?

【问题讨论】:

  • 子类化它并覆盖method()并使用super()调用基类的method()
  • 您是否有理由不只是创建一个子类?
  • 对不起,我忘了说我不能继承MyClass

标签: python monkeypatching


【解决方案1】:

是的,它被称为猴子补丁。

这基本上是装饰,但在类已经定义后手动完成。

from functools import wraps

def wrapper(f):
    @wraps(f)
    def wrapped(*args, **kwargs):
        myFunction()
        return f(*args, **kwargs)
    return wrapped

MyClass.printThis = wrapper(MyClass.printThis)

它将影响MyClass 的所有实例,即使是那些在应用补丁之前创建的实例。

如果您不需要动态修改运行时行为,请避免猴子修补,并且更喜欢使用继承来自定义行为,如 cmets 中所建议的那样。它不那么骇人听闻。

【讨论】:

    【解决方案2】:

    这是wim's answer 的替代方案,还涉及猴子补丁。但是,它是通过unittest.mock 提供的功能来实现的。这种方法的优点是上下文管理器用于在有限范围内自动应用和删除补丁:

    from unittest import mock
    
    # This class would be defined in some third-party library
    class MyClass:
        def method(self, msg):
            print('from method:', msg)
    
    
    def function(msg):
        print('from function:', msg)
    
    
    old_method = MyClass.method
    
    
    def new_method(self, msg):
        old_method(self, msg)
        function(msg)
    
    
    # The patch is only applied within this scope
    with mock.patch.object(MyClass, 'method', new_method):
        foo = MyClass()
        foo.method('message with patched')
    
    # By this point MyClass is "back to normal"
    print('---')
    foo.method('message with original')
    

    输出

    from method: message with patched
    from function: message with patched
    ---
    from method: message with original
    

    【讨论】:

      【解决方案3】:

      你也可以继承它:

      class MyClass:
          def method(self, msg):
              print 'from method:', msg
      
      def function(msg):
          print 'from function:', msg
      
      class MyNewClass(MyClass):
          def method(self, msg):
              function(msg)
              MyClass.method(self, msg)
      

      并像这样使用它:

      >>> a = MyNewClass()
      >>> a.method("test")
      from function: test
      from method: test
      

      或者,如果你想让你的类成为“新式”类(对于 Python 2 - 根据你的打印语句判断) - 只需让 MyClass 继承自 object,然后你可以使用用户 super

      class MyClass(object):  # object added here
          def method(self, msg):
              print 'from method:', msg
      
      def function(msg):
          print 'from function:', msg
      
      class MyNewClass(MyClass):
          def method(self, msg):
              function(msg)
              super(self.__class__, self).method(msg)  # super added here
      

      【讨论】:

      • 我可能会用super(MyNewClass, self).method(msg) 调用它,不确定它是否适用于您的调用方案(方法不是类方法)
      • @boatcoder super 不起作用(在 Python2 中),除非该类是新样式类(OP 的示例不是基于打印的 py2)。如果他们想适应新风格,则添加示例:)
      • 很好地了解了那个老式课程,我什至没有注意到。
      【解决方案4】:

      结束了这个解决方案,直到发布更好的解决方案......

      class MyClass:
          def method(self, msg):
              print 'from method:', msg
      
      def function(msg, callback):
          print 'from function:', msg
          callback(msg)
      
      foo = MyClass()
      foo.function = function
      foo.function(msg='message', callback=foo.method)
      

      【讨论】:

      • 每次调用method() 时如何调用function()
      • 这里是method(),每次调用function时都会通过callback调用。
      猜你喜欢
      • 2010-12-14
      • 1970-01-01
      • 1970-01-01
      • 2013-07-04
      • 2021-09-20
      • 1970-01-01
      • 2021-02-19
      • 1970-01-01
      • 2017-05-08
      相关资源
      最近更新 更多