On Sun, 25 Sep 2005 14:52:56 +0000, Ron Adam wrote: > Steven D'Aprano wrote: > > >> Or you could put the method in the class and have all instances recognise >> it: >> >> py> C.eggs = new.instancemethod(eggs, None, C) >> py> C().eggs(3) >> eggs * 3 > > Why not just add it to the class directly? You just have to be sure > it's a class and not an instance of a class.
Because I started off explicitly adding functions to instances directly, and when I discovered that didn't work properly, I never even tried adding it to the class until after I discovered that instancemethod() worked. As far as I can see, Python's treatment of functions when you dynamically add them to classes and instances is rather confused. See, for example: py> class Klass: ... pass ... py> def eggs(self, x): ... print "eggs * %s" % x ... py> inst = Klass() # Create a class instance. py> inst.eggs = eggs # Dynamically add a function/method. py> inst.eggs(1) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: eggs() takes exactly 2 arguments (1 given) >From this, I can conclude that when you assign the function to the instance attribute, it gets modified to take two arguments instead of one. Test it by explicitly passing an instance: py> inst.eggs(inst, 1) eggs * 1 My hypothesis is confirmed. Can we get the unmodified function back again? py> neweggs = inst.eggs py> neweggs(1) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: eggs() takes exactly 2 arguments (1 given) Nope. That is a gotcha. Storing a function object as an attribute, then retrieving it, doesn't give you back the original object again. So while you can do this: def printgraph(f): # print a graph of a function parameters = get_params() draw_graph(f, parameters) you can't do this: def printgraph(function): # print a graph of a function parameters = get_params() parameters.function = f # WARNING: f is modified here draw_graph(parameters) When storing the function object as an instance object, it is half-converted to a method: even though eggs is modified to expect two arguments, Python doesn't know enough to automatically pass the instance object as the first argument like it does when you call a true instance method. Furthermore, the type of the attribute isn't changed: py> type(eggs) <type 'function'> py> type(inst.eggs) <type 'function'> But if you assign a class attribute to a function, the type changes, and Python knows to pass the instance object: py> Klass.eggs = eggs py> inst2 = Klass() py> type(inst2.eggs) <type 'instancemethod'> py> inst2.eggs(1) eggs * 1 The different behaviour between adding a function to a class and an instance is an inconsistency. The class behaviour is useful, the instance behaviour is broken. > >>> def beacon(self, x): > ... print "beacon + %s" % x > ... Did you mean bacon? *wink* > >>> C.beacon = beacon > >>> dir(A) > ['__doc__', '__module__', 'beacon', 'ham', 'spam'] Okay, you aren't showing all your code. What is A? -- Steven. -- http://mail.python.org/mailman/listinfo/python-list