Injecting methods into instance / class
Peter Otten
__peter__ at web.de
Sun Dec 2 13:36:12 EST 2018
duncan smith wrote:
> Hello,
> I have a lot of functions that take an instance of a particular
> class as the first argument. I want to create corresponding methods in
> the class. I have tried the following, which (when called from __init__)
> creates the relevant methods in an instance (Python 3.6).
>
>
> def init_methods(self):
> for func_name, method_name in [('add', '__add__'),
> ('subtract', '__sub__')]:
> setattr(self, method_name,
> types.MethodType(globals()[func_name], self))
>
>
> The problem is that e.g.
>
> x.__sub__(y)
>
> works as expected, but
>
> x - y
>
> does not (because special methods are looked up in the class rather than
> the instance).
>
> I have tried to find examples of injecting methods into classes without
> success. I have tried a few things that intuition suggested might work,
> but didn't - like removing the first line above, dedenting and replacing
> self with the class. This is only to save typing and make the code
> cleaner, but I would like to get it right. Any pointers appreciated. TIA.
You can do this in the __ init__() method:
$ cat tmp.py
def add(self, other):
return 42 + other
def init_methods(self):
cls = type(self)
for methodname, funcname in [("__add__", "add")]:
func = globals()[funcname]
setattr(cls, methodname, func)
class Demo:
def __init__(self):
init_methods(self)
x = Demo()
print(x + 100)
$ python3 tmp.py
142
but the __add__() method (to take the example above) is shared by all
instances. You are either doing too much work by setting it again and again
in every Demo.__init__() call or you are changing the behaviour of
previously initialised Demo instances (if the last init_methods() sets the
method to a different function) and confuse yourself.
Have you considered a mix-in class instead? Like
$ cat tmp.py
def add(self, other):
return 42 + other
class CommonMethods:
__add__ = add
class Demo(CommonMethods):
pass
x = Demo()
print(x + 100)
$ python3 tmp.py
142
This is much cleaner, and if you insist you can set up CommonMethods
programmatically with
CommonMethods = type(
"CommonMethods", (),
{m: globals()[f] for m, f in [("__add__", "add")]}
# without globals() and probably better:
# {m: f for m, f in [("__add__", add)]}
)
though personally I don't see the point.
More information about the Python-list
mailing list