Welcome, guest | Sign In | My Account | Store | Cart

Ruby has the functionality of being able to add a method to a class at an arbitrary point in your code. I figured Python must have some way for allowing this to happen, and it turned out it did. The method is available instantly to all already existing instances and of course ones yet to be created. If you specify method_name then that name is used for the method call.

One thing to make sure to do is that the function has a variable for the instance to be passed to (i.e. self).

Python, 8 lines
1
2
3
4
5
6
7
8
def funcToMethod(func,clas,method_name=None):
    """Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
    The new method is accessible to any instance immediately."""
    func.im_class=clas
    func.im_func=func
    func.im_self=None
    if not method_name: method_name=func.__name__
    clas.__dict__[method_name]=func

Why would one use this? I am not completely sure. You could use it as a way to get around having to deal with inheritence. But as I said in the summary, this was mainly to show that it was possible and how flexible Python is.

6 comments

Alex Martelli 20 years, 1 month ago  # | flag

module new may be preferable. By binding or re-binding attributes of object func, this recipe is fragile -- if funcToMethod is called twice on the same func, confusion is quite possible. Module new is designed to help exactly with such dynamic needs:

def funcToMethod(func,clas,method_name=None):
    import new
    method = new.instancemethod(func,None,clas)
    if not method_name: method_name=func.__name__
    clas.__dict__[method_name]=method

A somewhat mind-boggling (or mind-expanding:-) extra of this approach is that func can in fact be any callable, such as an instance of any class that supplies a __call_ special method, or a bound-method...

The 'instancemethod' function name may be slightly misleading: it generates both bound and unbound methods, depending on whether the second argument is None (unbound) or an instance of the class that is the third argument. See http://www.python.org/doc/current/lib/module-new.html for all the details (there's not much more to it than this, though).

Brett Cannon (author) 20 years, 1 month ago  # | flag

Should Have Read the Docs. This is what happens when you get too excited about writing code and don't bother to plan far enough ahead to read the documentation.

Thank you for pointing out the more proper way of handling this, Alex.

Peter Shannon 19 years, 1 month ago  # | flag

Using a function wrapper to automatically provide self to methods added at runtime. The problem with only adding a new function to a class instance's dictionary, is it must always be called in a special manner - it dosn't behave in the same way as normal methods. Using a wrapper class can get round this problem providing a more transparent solution.

The function to be added is say(). The parameter host performs the same purpose as what we normally refer to as self - the object which this method works on. The name host simply reminds us we are parasites in this place!

#!/usr/bin/env python

def say(host, msg):
   print '%s says %s' % (host.name, msg)

def funcToMethod(func, clas, method_name=None):
   setattr(clas, method_name or func.__name__, func)

class transplant:
   def __init__(self, method, host, method_name=None):
      self.host = host
      self.method = method
      setattr(host, method_name or method.__name__, self)

   def __call__(self, *args, **kwargs):
      nargs = [self.host]
      nargs.extend(args)
      return apply(self.method, nargs, kwargs)

class Patient:
   def __init__(self, name):
      self.name = name

if __name__ == '__main__':
   jimmy = Patient('Jimmy')
   transplant(say, jimmy, 'say1')
   funcToMethod(say, jimmy, 'say2')

   jimmy.say1('Hello')
   jimmy.say2(jimmy, 'Good Bye!')
Greg Anderson 15 years, 4 months ago  # | flag

Synchronizing instance methods. Slight modification to the recipe "9.1 Synchronizing All Methods in an Object." Whereas the the former synchronizes all instances of a class on a single lock, this code synchronizes each instance on it's own lock to makes sure that only one thread at a time has access to certain bound methods. Unsynchronized methods behave normally. Plus, I think this version is more clear.

import threading

Just as it is in recipe 9.1 of Python Cookbook, v2

def wrap_callable(any_callable, before, after, new_name=None): """ Wrap any callable with before/after calls """ def _wrapped(args, *kwds): before() try: return any_callable(args, *kwds) finally: after() _wrapped.__name__ = new_name and new_name or any_callable.__name__ return _wrapped

if __name__ == '__main__': import threading, time

class A(object):
    def __init__(self):
        self.lock = threading.Lock()
        # Synchronize these on self.lock, and make them unbound/bound methods
        A.foo = wrap_callable(A._foo, self.lock.acquire, self.lock.release)
        A.bar = wrap_callable(A._bar, self.lock.acquire, self.lock.release)
    def _foo(self):
        print 'I am foo'
        time.sleep(2)
    def _bar(self):
        print 'I am bar'
    def baz(self):
        print 'I am baz'
a = A()

print A._foo, A.foo
print a._foo, a.foo
print a._foo.__name__, a.foo.__name__
print type(a._foo), type(a.foo)


threading.Thread(target=a.foo).start()
time.sleep(0.2)
threading.Thread(target=a.bar).start()
time.sleep(0.2)
threading.Thread(target=a.baz).start()
timepass man 12 years, 11 months ago  # | flag

simple way

class Foo:

def __init__(self):
    self.x = "x = 1"
    self.y = "y = 2"

def showx(self):
    print self.x

def showy(self): print self.y

if __name__ == '__main__':

# Attach at runtime
Foo.showy = showy

f2 = Foo()
f2.showx()
f2.showy()
dechico marc 11 years ago  # | flag
class Foo:
    function=None
    def __init__(self):
        self.function= Foo.f1

    def f1(self):
        print "f1"

f=Foo()
f.function(f)

if I want to use another function than f1

def f2(self):
    print "f2"
f.function=f2
f.function(f)

I could also initialize function at class initialisation

but I still will need to pass the class instance as first argument.

tested on python 2.6.2

Created by Brett Cannon on Mon, 15 Oct 2001 (PSF)
Python recipes (4591)
Brett Cannon's recipes (16)

Required Modules

  • (none specified)

Other Information and Tasks