Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

functools.partial on class method

I'm trying to define some class methods using another more generic class method as follows:

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.partial(_color, type='_red')     blue = functools.partial(_color, type='_blue')     green = functools.partial(_color, type='_green') 

But when i attempt to invoke any of those methods i get:

rgb = RGB(100, 192, 240) print rgb.red() TypeError: _color() takes exactly 2 arguments (1 given) 

I guess self is not passed to _color since rgb.red(rgb) works.

like image 399
Arjor Avatar asked May 18 '13 16:05

Arjor


People also ask

What does partial do in Functools?

You can create partial functions in python by using the partial function from the functools library. Partial functions allow one to derive a function with x parameters to a function with fewer parameters and fixed values set for the more limited function. This code will return 8.

What does Functools do in python?

The functools module, part of Python's standard Library, provides useful features that make it easier to work with high order functions (a function that returns a function or takes another function as an argument ).

How does Functools cache work?

The value in the cache is stored as a list of four items(remember root). The first item is the reference to the previous item, the second item is the reference to the next item, the third item is the key for the particular function call, the fourth item is a result.

How do you use Lrucacache?

One way to implement an LRU cache in Python is to use a combination of a doubly linked list and a hash map. The head element of the doubly linked list would point to the most recently used entry, and the tail would point to the least recently used entry.


2 Answers

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.

like image 157
Martijn Pieters Avatar answered Sep 30 '22 00:09

Martijn Pieters


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.partial def 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.

like image 22
Conchylicultor Avatar answered Sep 29 '22 23:09

Conchylicultor