Monkey修补可以替换类中现有的函数定义吗?

时间:2017-03-17 11:18:25

标签: python monkeypatching

我知道SO社区是多么激烈,所以我会尽力保留问题minimal, complete and verifiable

我只想知道是否可以使用猴子补丁替换现有函数的定义?

例如:

class A():

    def foo():
       print '2'

def foo():
    print '5'

A.foo = foo

这种方式似乎也不起作用为什么我不添加新函数而不是替换现有函数,我在其他类中调用这些函数,我理解猴子修补在运行时添加这些函数,我需要我的python代码在Apache spark服务器上运行,该服务器抛出错误,认为对该函数的调用未引用。

所以请你好好帮助我或建议一个解决方案。 谢谢。

编辑:代码的目标是在调用A.foo时打印5。

2 个答案:

答案 0 :(得分:2)

我希望我明白你在这里想做什么。这适用于 Python 3

class A():

  def foo():
     print('2')

def foo():
  A.foo = lambda: print('5')

A.foo() # Print '2'
foo()   # Assign the new method
A.foo() # Prints '5'

Python 2 中,但有几点需要注意。

所以你必须这样做:

from __future__ import print_function

class A():

  def foo():
    print('2')

def foo():
  A.foo = lambda: print('5')

A.foo.__func__() # Print '2'
foo()   # Assign the new method
A.foo.__func__() # Prints '5'

修改 在评论中看到你的问题后,我认为你真的想要一些不同的东西。这是:

class A():

    def foo(self):
       print '2'

def foo(self):
  print '5'

a = A()
a.foo() # Print '2'
A.foo = foo   # Assign the new method
a.foo() # Prints '5'

这在 Python 2 中运行良好。

self是对方法绑定的当前实例的引用。当您只调用print之类的内容来访问附加到该实例的任何属性或方法时,不会使用它。但是对于不同的情况,请看下面的例子:

class A():

    msg = "Some message"

    def foo(self):
       print self.msg


def bar(self):
  self.msg = "Some other message"

a = A()
a.foo() # Print old msg
A.bar = bar   # Assign the new method
a.bar() # Assigns new message
a.foo() # Prints new message

此外,正如chepner在其帖子中的评论中指出的那样:

  

自我这个名字并不特别;它只是一个惯例。你可以用   这个如果你真的想要,它不需要是同一个名字   在这里定义的两个函数中。重要的是第一个   用作实例方法的函数的参数将是一个参考   到调用对象。 a.foo()几乎与A.foo(a)

完全相同

答案 1 :(得分:2)

您唯一的问题是您没有正确定义foo。它需要为调用它的实例采用显式参数。

class A(object):
    def __init__(self)
        self.x = 2

    def foo(self):
        print(self.x)

def foo(this):
    print(this.x + 3)

A.foo = foo

a = A()
a.foo()  # outputs 5 in Python 2 and Python 3

在一个非常真实的意义上,猴子修补是首先创建类的方式。 class语句几乎只是以下代码的语法糖:

def foo(self):
    print(self.x)
A = type('A', (object,), {'foo': foo})
del foo

type的定义进行成像并不是一个简单的简化

def type(name, bases, d):
    new_class = magic_function_to_make_a_class()
    new_class.name = name
    new_class.bases = bases
    for k, v in d.items():
        setattr(new_class, k, v)
    return new_class