Using metaclasses, I am trying to create an instance method by simplifying an existing instance method. The problem is that partial does not work with instance method. This is a simple example of what I try 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 the output is:
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 have solved using lambda changing:
setattr(cls, 'simple', partial(cls.complex, b=1))
to:
setattr(cls, 'simple', lambda self, x: cls.complex(self, x, b=1))
but it is ugly and has problems with optional parameters.
I could create these method at the instance __init__
but I guess it makes more sense, and is more efficient to do it on class __init__
using metaclasses.
Any ideas how to do it properly?
Well, I am a bit unfamiliar with Python 3 method handling yet -
the simplest thing I could think of is rewriting partial
so that it preserves the first argument from the original call, then inserts 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))
Instead of using partial
, I'd just define class Atype
like this:
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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With