23

I want to assign a function implementation dynamically.

Let's start with the following:

class Doer(object):

    def __init__(self):
        self.name = "Bob"

    def doSomething(self):
        print "%s got it done" % self.name

def doItBetter(self):
    print "Done better"

In other languages we would make doItBetter an anonymous function and assign it to the object. But no support for anonymous functions in Python. Instead, we'll try making a callable class instance, and assign that to the class:

class Doer(object):

    def __init__(self):
        self.name = "Bob"

class DoItBetter(object):

    def __call__(self):
        print "%s got it done better" % self.name

Doer.doSomething = DoItBetter()
doer = Doer()
doer.doSomething()

That gives me this:

Traceback (most recent call last): Line 13, in doer.doSomething() Line 9, in call print "%s got it done better" % self.name AttributeError: 'DoItBetter' object has no attribute 'name'

Finally, I tried assigning the callable to the object instance as an attribute and calling it:

class Doer(object):

    def __init__(self):
        self.name = "Bob"

class DoItBetter(object):

    def __call__(self):
        print "%s got it done better" % self.name


doer = Doer()
doer.doSomething = DoItBetter()
doer.doSomething()

This DOES work as long as I don't reference self in DoItBetter, but when I do it gives me an name error on self.name because it's referencing the callable's self, not the owning class self.

So I'm looking for a pythonic way to assign an anonymous function to a class function or instance method, where the method call can reference the object's self.

Yarin
  • 173,523
  • 149
  • 402
  • 512
  • Nothing wrong with the very first approach that you rejected. Why are you making it so complex? – David Heffernan Apr 29 '12 at 18:00
  • 2
    Your error is really odd - are you trying to run a Python script with a ruby interpreter? (Your file is called `prog.rb`, `.py` is the Python file extension). The error definitely isn't from Python - `self` isn't a keyword in Python. – Gareth Latty Apr 29 '12 at 18:02
  • My mistake- ran it on an online codepad with ruby settings- fixed – Yarin Apr 29 '12 at 18:19
  • 1
    @David- you're right- I was making it way too complicated – Yarin Apr 29 '12 at 18:20

2 Answers2

39

Your first approach was OK, you just have to assign the function to the class:

class Doer(object):
    def __init__(self):
        self.name = "Bob"

    def doSomething(self):
        print "%s got it done" % self.name

def doItBetter(self):
    print "%s got it done better" % self.name

Doer.doSomething = doItBetter

Anonymous functions have nothing to do with this (by the way, Python supports simple anonymous functions consisting of single expressions, see lambda).

Yarin
  • 173,523
  • 149
  • 402
  • 512
yak
  • 8,851
  • 2
  • 29
  • 23
39

yak's answer works great if you want to change something for every instance of a class.

If you want to change the method only for a particular instance of the object, and not for the entire class, you'd need to use the MethodType type constructor to create a bound method:

from types import MethodType

doer.doSomething = MethodType(doItBetter, doer)
LeWoody
  • 3,593
  • 4
  • 25
  • 31
Amber
  • 507,862
  • 82
  • 626
  • 550
  • 2
    At least in Python 3, it should be `doer.doSomething = MethodType(doItBetter, doer)` . A good explanation of the different cases for methods and functions is given in http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance – yanlend Mar 23 '16 at 07:58
  • @yanlend this question was asked for Python 2. – Amber Mar 24 '16 at 00:23