5

I'm using functools.partial to create a closure, and using setattr to make is callable from a class instance. The idea here is to create a set of methods at runtime.

#!/usr/bin/python
from functools import partial

class MyClass(object):

    def __init__(self, val):
        self.val = val

    @classmethod
    def generateMethods(self):
        def dummy(conf1, self):
            print "conf1:", conf1
            print "self.val:", self.val
            print

        for s in ('dynamic_1', 'dynamic_2'):
            closed = partial(dummy, s)
            setattr(self, "test_{0}".format(s), closed)

It seems to me that partial would bind the current value of s to dummy's first arg, which would free up self to be passed when this is called from an instance.

It's not working how I'd expect

if __name__ == '__main__':
    # Dynamically create some methods
    MyClass.generateMethods()

    # Create an instance
    x = MyClass('FOO')

    # The dynamically created methods aren't callable from the instance :(
    #x.test_dynamic_1()
    # TypeError: dummy() takes exactly 2 arguments (1 given)

    # .. but these work just fine
    MyClass.test_dynamic_1(x)
    MyClass.test_dynamic_2(x)

Is it possible to dynamically create methods which are closures, but callable from instances of the class?

ajwood
  • 18,227
  • 15
  • 61
  • 104
  • possible duplicate of [Adding a Method to an Existing Object](http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object) – Nir Alfasi Jul 17 '15 at 00:35
  • @alfasin that question asks about adding methods to specific instances; my question asks about adding methods to a class, to be inherited by all instances – ajwood Jul 17 '15 at 12:35
  • True, but the accepted answer shows how to do both – Nir Alfasi Jul 17 '15 at 15:04

2 Answers2

10

I think the new functools.partialmethod is for this exact use case.

Straight from the docs:

>>> class Cell(object):
...     def __init__(self):
...         self._alive = False
...     @property
...     def alive(self):
...         return self._alive
...     def set_state(self, state):
...         self._alive = bool(state)
...     set_alive = partialmethod(set_state, True)
...     set_dead = partialmethod(set_state, False)
...
>>> c = Cell()
>>> c.alive
False
>>> c.set_alive()
>>> c.alive
True
dcmorse
  • 1,011
  • 11
  • 15
5

The issue is that when you're calling them using the instances they are actually not bound methods, i.e they have no knowledge about the instance. Bound methods insert the self to the arguments of the underlying function automatically when called, it is stored in the __self__ attribute of bound method.

So, override __getattribute__ and see if the object being fetched is an instance of partial type or not, if yes, convert it to a bound method using types.MethodType.

Code:

#!/usr/bin/python
from functools import partial
import types


class MyClass(object):

    def __init__(self, val):
        self.val = val

    @classmethod
    def generateMethods(self):
        def dummy(conf1, self): 
            print "conf1:", conf1
            print "self.val:", self.val
            print

        for s in ('dynamic_1', 'dynamic_2'):
            closed = partial(dummy, s)
            setattr(self, "test_{0}".format(s), closed)

    def __getattribute__(self, attr):
        # Here we do have access to the much need instance(self)
        obj = object.__getattribute__(self, attr)
        if isinstance(obj, partial):    
            return types.MethodType(obj, self, type(self))
        else:
            return obj


if __name__ == '__main__':
    MyClass.generateMethods()

    x = MyClass('FOO')

    x.test_dynamic_1()
    x.test_dynamic_2()
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • This works! I guess there's no way of adding the closure as a `MethodType` right off the bat, rather than converting it on the fly when it's called? – ajwood Jul 17 '15 at 13:03