Gregory Bond wrote: > Thanks Peter, that's a big help.
You're welcome. > I can solve my problem now, but I'm chasing this further in the name of > education, because it seems there is some deep magic happening here that > I don't understand. Python resorts to deep magic only when it's inevitable, i. e. hardly ever :-) > It seems that applying staticfunction() (or perhaps assigning to the > class object) is treated differently if it happens in the class > defininition, to when it happens at "run time". (My previous attempts > to get staticmember() to do the right thing were all "run time" like the > examples below.) There is no distinction between "run time" and "class definition time". The class "body" is actually a function that is called when the module is loaded. Its locals() are then used to create a type instance, i. e. the class. > Say I wanted to keep the need for "staticmember" hidden from subclasses, > and do the staticmember() conversion in the base class methods. I've > tried 2 ways of doing this (no subclassing here, just to keep it simple): > >> class B(object): >> fn = foo >> def try1(self): >> print "B.fn is", type(B.fn) >> B.fn = staticmethod(B.fn) >> print "B try1" >> print "B.fn is now", type(B.fn) >> B.fn() >> >> def try2(self): >> fn2 = staticmethod(B.fn) >> print "B try2" >> print "fn2 is now", type(fn2) >> fn2() > > If I try method 1 (assigning to the class object - ignore for a moment > the problem of only doing this once!) I get a set of very surprising > results: > >> B.fn is <type 'instancemethod'> >> B try1 >> B.fn is now <type 'instancemethod'> >> Traceback (most recent call last): >> File "t_o1.py", line 28, in ? >> B().try1() >> File "t_o1.py", line 17, in try1 >> B.fn() >> TypeError: unbound method foo() must be called with B instance as first >> argument (got nothing instead) > > note that assigning the staticmember() result to B.fn does NOT change > the type of B.fn!! And foo is treated as a member function. > > So if I try method 2 (using staticmethod() at runtime): > >> B try2 >> fn2 is now <type 'staticmethod'> >> Traceback (most recent call last): >> File "t_o1.py", line 27, in ? >> B().try2() >> File "t_o1.py", line 22, in try2 >> fn2() >> TypeError: 'staticmethod' object is not callable > > fn2 is a static method, as I'd expect, but it is somehow not callable? Your problems stem from the fact that attribute assignment to a class doesn't always roundtrip: >>> A.value = 42 >>> A.value 42 >>> def f(): pass ... >>> A.method = f >>> A.method, f (<unbound method A.f>, <function f at 0x4028eae4>) If a class attribute is a descriptor i. e. it has a __get__() method (which pure python functions do) A.method is not just a dictionary lookup but a call A.__dict__["method"].__get__(None, A) This is slightly simplified as it doesn't take inheritance into account. __get__() is free to apply whatever magic, but staticmethod just gives back the original function whereas a some_func.__get__(None, SomeClass) gives you an unbound method: >>> staticmethod(f).__get__(None, A) <function f at 0x4028eae4> >>> f.__get__(None, A) <unbound method A.f> Is there a way to get the original function back out of the unbound method? Let's see: >>> dir(A.method) ['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'im_class', 'im_func', 'im_self'] im_func seems promising: >>> A.method.im_func <function f at 0x4028eae4> > Can someone explain what is going on here? Pointers to language spec, > code, PEPs etc gladly accepted. > > Greg, > caught in a twisty little maze of descriptors, all different! The two relevant documents are http://users.rcn.com/python/download/Descriptor.htm http://www.python.org/2.2.1/descrintro.html Peter -- http://mail.python.org/mailman/listinfo/python-list