Create an instance method in a metaclass using partial in Python 3

Using metaclasses, I'm trying to create an instance method by simplifying an existing instance method. The problem is that partial does not work with the instance method. This is a simple example of what I'm trying to achieve:

from functools import partial class Aclass(object): def __init__(self, value): self._value = value def complex(self, a, b): return a + b + self._value class Atype(type): def __new__(cls, name, bases, attrs): return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs) def __init__(cls, name, bases, attrs): setattr(cls, 'simple', partial(cls.complex, b=1)) class B(metaclass=Atype): pass b = B(10) print(b.complex(1, 2)) print(b.simple(1)) 

and output:

 13 Traceback (most recent call last): File "metatest.py", line 22, in <module> print(b.simple(1)) TypeError: complex() takes exactly 3 non-keyword positional arguments (1 given) 

I decided to use lambda change:

  setattr(cls, 'simple', partial(cls.complex, b=1)) 

in

  setattr(cls, 'simple', lambda self, x: cls.complex(self, x, b=1)) 

but it is ugly and has problems with additional parameters.

I could create this method in an __init__ instance, but I think it makes more sense and is more efficient for this in the __init__ class using metaclasses.

Any ideas how to do this correctly?

+7
source share
3 answers

Well, I'm a little unfamiliar with the handling of the Python 3 method - the simplest thing I could think of is rewriting partial so that it retains the first argument from the original call and then introduces the "partial" parameters.

It worked with your example, but it needs testing with more complex patterns.

 from functools import wraps class Aclass(object): def __init__(self, value): self._value = value def complex(self, a, b): return a + b + self._value def repartial(func, *parameters, **kparms): @wraps(func) def wrapped(self, *args, **kw): kw.update(kparms) return func(self, *(args + parameters), **kw) return wrapped class Atype(type): def __new__(cls, name, bases, attrs): return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs) def __init__(cls, name, bases, attrs): setattr(cls, 'simple', repartial(cls.complex, b=1)) class B(metaclass=Atype): pass b = B(10) print(b.complex(1, 2)) print(b.simple(1)) 
+5
source

Instead of using partial I would simply define the Atype class as follows:

 class Atype(type): def __new__(cls, name, bases, attrs): return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs) def __init__(cls, name, bases, attrs): def simple(self, a): return cls.complex(self, a, 1) setattr(cls, 'simple', simple) 

The __init__() method can also be written more compactly:

 def __init__(cls, name, bases, attrs): setattr(cls, 'simple', lambda self, a: cls.complex(self, a, 1)) 
+3
source

The problem is that the object returned by functools.partial() is a called object, not a function. Therefore, apparently, Python does not care about a non-function trying to act as one in this context. One solution is to create a function as a wrapper for a partial object.

 class Atype(type): def __init__(cls, name, bases, attrs): simple = partial(cls.complex, b=1) setattr(cls, 'simple', lambda cls, a: simple(cls, a)) 

jsbueno solution (a reimplementation of partial that returns a real function) is good. I really don't know why functools.partial() does not work this way; failure to use it in this context is an amazing trap.

+1
source

All Articles