functools.partial on class method functools.partial on class method python python

functools.partial on class method


You are creating partials on the function, not the method. functools.partial() objects are not descriptors, they will not themselves add the self argument and cannot act as methods themselves. You can only wrap bound methods or functions, they don't work at all with unbound methods. This is documented:

partial objects are like function objects in that they are callable, weak referencable, and can have attributes. There are some important differences. For instance, the __name__ and __doc__ attributes are not created automatically. Also, partial objects defined in classes behave like static methods and do not transform into bound methods during instance attribute look-up.

Use propertys instead; these are descriptors:

class RGB(object):    def __init__(self, red, blue, green):        super(RGB, self).__init__()        self._red = red        self._blue = blue        self._green = green    def _color(self, type):        return getattr(self, type)    @property    def red(self): return self._color('_red')    @property    def blue(self): return self._color('_blue')    @property    def green(self): return self._color('_green')

As of Python 3.4, you can use the new functools.partialmethod() object here; it'll do the right thing when bound to an instance:

class RGB(object):    def __init__(self, red, blue, green):        super(RGB, self).__init__()        self._red = red        self._blue = blue        self._green = green    def _color(self, type):        return getattr(self, type)    red = functools.partialmethod(_color, type='_red')    blue = functools.partialmethod(_color, type='_blue')    green = functools.partialmethod(_color, type='_green')

but these'd have to be called, whilst the property objects can be used as simple attributes.


The issue with partialmethod is that it is not compatible with inspect.signature, functools.wraps,...

Weirdly enough, if you re-implement functools.partial yourself using the partial documentation implementation example, it will work:

# Implementation from:# https://docs.python.org/3/library/functools.html#functools.partialdef partial(func, /, *args, **keywords):    def newfunc(*fargs, **fkeywords):        newkeywords = {**keywords, **fkeywords}        return func(*args, *fargs, **newkeywords)    newfunc.func = func    newfunc.args = args    newfunc.keywords = keywords    return newfunc
class RGB(object):    def __init__(self, red, blue, green):        super(RGB, self).__init__()        self._red = red        self._blue = blue        self._green = green    def _color(self, type):        return getattr(self, type)    red = partial(_color, type='_red')    blue = partial(_color, type='_blue')    green = partial(_color, type='_green')rgb = RGB(100, 192, 240)print(rgb.red())  # Print red

The reason is that newfunc is a true function which implement the descriptor protocol with newfunc.__get__. While type(functools.partial) is a custom class with __call__ overwritten. Class won't add the self parameter automatically.