Search code examples
pythonfunctoolspartial-application

How to dynamically add method to class with `functools.partial()`


I am having trouble with the right incantation to get a dynamic method added to a class using functools.partial in the following situation. The following has a Creator class to which I want to add a create_someclass method, which is partially parameterized by the creator class state.

import functools

class Creator:
    def __init__(self, params):
        self.params = params

class Stitch:
    __tablename__ = 'stitch'
    def __init__(self, params, name):
        self.name = name
        self.params = params

def create(self, clz, *args, **kwargs):
    return clz(self.params, *args, **kwargs)

for clazz in [Stitch]:
    setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partial(create, clz=clazz))

creator = Creator('params')

# Neither of these work, but I'd like either one -- preferably the first one.

stitch = creator.create_stitch('myname')
# AttributeError: 'str' object has no attribute 'params'

stitch = creator.create_stitch(name='myname')  
# TypeError: create() missing 1 required positional argument: 'self'

Solution

  • This is a problem for making partial for class methods, so in Python 3.4 we introduced partialmethod as the alternative. The way that works is the following:

    import functools
    
    class Creator:
        def __init__(self, params):
            self.params = params
    
    class Stitch:
        __tablename__ = 'stitch'
        def __init__(self, params, name):
            self.name = name
            self.params = params
    
    def create(self, clz, *args, **kwargs):
        return clz(self.params, *args, **kwargs)
    
    for clazz in [Stitch]:
        setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partialmethod(create, clz=clazz))
        # use partialmethod instead here
    
    creator = Creator('params')
    
    stitch = creator.create_stitch(name='myname')  
    # works!