Vivek Shankar
Vivek Shankar

Reputation: 800

Can Monkey patching replace existing function definition in a class?

I know how fierce the SO community is so I'll try my best to keep the question minimal, complete and verifiable.

What I simply want to know is can monkey patching be used to replace the definition of an existing function?

for example:

class A():

    def foo():
       print '2'

def foo():
    print '5'

A.foo = foo

This way doesn't seem to work also as to why I don't just add a new function instead of replacing an existing one, I call these functions in other classes and it is my understanding that monkey patching adds those functions at run-time and I need my python code to run on an Apache spark server which throws an error deeming the calls to that function unreferenced.

So please be nice and help me out or suggest a work around. Thanks.

Edit: The goal of the code is to print 5 when A.foo is called.

Upvotes: 3

Views: 3377

Answers (2)

chepner
chepner

Reputation: 531135

Your only problem is that you aren't defining foo correctly in the first place. It needs to take an explicit argument for the instance calling it.

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

In a very real sense, monkey patching is how classes are created in the first place. A class statement is almost just syntactic sugar for the following code:

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

It's not too much of a simplification to image the definition of type being something like

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

Upvotes: 3

Sören Titze
Sören Titze

Reputation: 1005

I hope I understand what you are trying to do here. This would work in 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'

In Python 2 however there are several caveats.

So you have to do it like this:

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'

Edit: After seeing your question in the comment I think you actually want something different. Which is this:

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'

This works just fine in Python 2.

The self is a reference to the current instance the method is bound to. It is not used when you just call something like print which access any properties or methods attached to that instance. But for a different case please have a look at the following example:

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

Also as chepner points out in a comment under his post:

The name self isn't special; it's just a convention. You could use this if you really wanted to, and it doesn't need to be the same name in both functions defined here. What is important is that the first argument to a function used as an instance method will be a reference to the calling object. a.foo() is almost exactly the same as A.foo(a)

Upvotes: 2

Related Questions