Kannan Varadhan wrote: > Hi Folks, > > Here is something I don't fully understand. > > 1 def DefaultTracer(fmt, *args): > 2 fmt = 'in DefaultTracer ' + fmt > 3 print(fmt % args) > 4 pass > 5 def myTracer(fmt, *args): > 6 fmt = 'in myTracer ' + fmt > 7 print(fmt % args)
Please don't post code with line numbers. That makes it difficult to copy and paste your function into an interactive session, so that we can run it and see what it does. [...] > I want ClassDefaultTracer to store a reference to DefaultTracer and be > used by instances of MyClass. Why does line20 give me the following > error: > > $ python foo.py > in DefaultTracer Testing at first > in myTracer Testing at first > Traceback (most recent call last): > File "foo.py", line 20, in <module> > MyClass().trace('Using ClassDefaultTracer') > File "foo.py", line 14, in trace > self.InstanceTracer(fmt, *args) > TypeError: unbound method DefaultTracer() must be called with MyClass > instance as first argument (got str instance instead) Unfortunately you've run into a side-effect of one of the very few "Do What I Mean" aspects of Python: under many circumstances, storing a function in a class automatically turns it into a method. This is almost always what you want, but not this time. Here's a simple example: >>> def function(s): ... print s ... >>> class C(object): ... func = function ... print func, type(func) ... <function function at 0xb7f892cc> <type 'function'> >>> print C.func, type(C.func) <unbound method C.function> <type 'instancemethod'> How to interpret this: The class block is an executable statement which is executed at runtime. During that execution, "print func" is called, and it sees func is an actual function object. After the block finishes executing, a class object (also called a type) is created. This is where the DWIM behaviour happens: function attributes are automatically turned into methods. That's normally what you want, but in this case, it gives you the surprising result: >>> C.func("hello world") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method function() must be called with C instance as first argument (got str instance instead) Instead of calling it from the class C, you can call it from an instance: >>> c = C() >>> c.func("hello world") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: function() takes exactly 1 argument (2 given) Two arguments? What? Where's the second argument??? But wait: *bound* methods automatically get the instance as their first argument. (When you define a method in the class, you write a function with "self" as the first argument.) A bound method is one that is called from an instance, not the class, and it gets the instance as first argument automatically: instance.method(args) # a bound method is translated by Python into: theclass = type(instance) theclass.method(instance, args) # an unbound method So if we do this, the method works: >>> c.func() # bound methods get the instance as first argument <__main__.C object at 0xb7f8fb6c> There are a couple of solutions to avoid this. One is to live with it: def function(self, s): # Define this as if it were a method. print s class C(object): func = function Now you can call: instance = C() # unbound method, you are responsible for supplying "self" C.func(instance, arg) # bound method, Python supplies "self" instance.func(arg) but of course now you can't call function(arg) from outside the class. Another solution is to use a staticmethod: def function(s): print s class C(object): func = staticmethod(function) A third is to disguise the function: class C(object): func = (function,) # In a tuple and then call C.func[0](arg) but that's hideous. Don't do that. Fourthly, the DWIM magic only happens when you store a function in the *class*, not if you do it in the instance: class C(object): def __init__(self): self.func = function but now you can't call C.func because it doesn't exist, you have to initialise an instance first. > Alternately, how can I achieve what I want, i.e. a class-wide default > used by all instances created off it, but > itself be changeable, so that once changed, the changed default would > be used by subsequent newer instances of that class. class C(object): default = staticmethod(function) def __init__(self): self.func = type(self).default ought to do it. -- Steven -- http://mail.python.org/mailman/listinfo/python-list