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?

3
  • possible duplicate of Adding a Method to an Existing Object Commented Jul 17, 2015 at 0: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 Commented Jul 17, 2015 at 12:35
  • True, but the accepted answer shows how to do both Commented Jul 17, 2015 at 15:04

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

Comments

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()

1 Comment

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?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.