On 02/12/2018 18:36, Peter Otten wrote: > 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. >
Ah, I could just bind them within the class, mean = mean max = max etc. but I'd somehow convinced myself that didn't work. As the names are the same I'd probably still prefer to go with something along the lines of for name in ['mean', 'max', ...]: # create a method But at least I now have something that works, and I could try something like your suggestion above to see if I prefer it. The issue was that some of these "functions" are actually callable class instances (an attempt to emulate numpy's ufuncs). class Func: def __init__(self, op): # op is a numpy ufunc self.op = op def __call__(self, *args, **kwargs): # some logic here to decide # which method to call on the # basis of the number of arguments # and return values def one_one(self, x): # the method to call for a single argument # and single return value Just rebinding these doesn't work, and these are the callables that need corresponding special methods. I guess I probably tried binding one of these first and convinced myself that it didn't work generally. Many thanks (to everyone who has responded). Not exactly solved, but I've made some progress. I might just write the methods normally, rather than looking for a clever shortcut. Cheers. Duncan -- https://mail.python.org/mailman/listinfo/python-list