Kannan Varadhan wrote: > 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) > 8 > 9 class MyClass: > 10 ClassDefaultTracer = DefaultTracer > 11 def __init__(self): > 12 self.InstanceTracer = MyClass.ClassDefaultTracer > 13 def trace(self, fmt, *args): > 14 self.InstanceTracer(fmt, *args) > 15 > 16 > 17 DefaultTracer("Testing %s", 'at first') > 18 myTracer("Testing %s", 'at first') > 19 > 20 MyClass().trace('Using ClassDefaultTracer') > 21 > 22 # override ClassDefaultTracer for all new instances > 23 MyClass.ClassDefaultTracer = myTracer > 24 MyClass().trace('Using Overridden ClassDefaultTracer') > > 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)
Functions written in Python are also "descriptors", they have a __get__() method. The seemingly harmless whatever = MyClass.ClassDefaultTracer therefore results in something like whatever = MyClass.__dict__["ClassDefaultTracer"].__get__(None, MyClass) internally, and whatever becomes an "unbound method", essentially the DefaultTracer function wrapped in a check that its first argument is a MyClass instance. The simplest workaround is probably to spell out the dictionary access whatever = MyClass.__dict__["ClassDefaultTracer"] If you want inheritance to work properly you need something more invoved, perhaps (untested) f = self.ClassDefaultTracer try: f = f.im_func except AttributeError: pass self.InstanceTracer = f or you wrap the callable in a descriptor: >>> def DefaultTracer(*args): print args ... >>> class D(object): ... def __init__(self, f): ... self.f = f ... def __get__(self, *args): ... return self.f ... >>> class MyClass(object): ... old = DefaultTracer ... new = D(DefaultTracer) ... >>> MyClass.old <unbound method MyClass.DefaultTracer> >>> MyClass.new <function DefaultTracer at 0x7f0f1c0c45f0> >>> m = MyClass() >>> m.old <bound method MyClass.DefaultTracer of <__main__.MyClass object at 0x7f0f1cda6fd0>> >>> m.new <function DefaultTracer at 0x7f0f1c0c45f0> Not pretty, but in Python 3 the problem is gone... > 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. -- http://mail.python.org/mailman/listinfo/python-list