[Python-3000] The case for unbound methods? Exhibit A, wraptest.py (demonstrates using a wrapper to insert an extra argument into method calls): ------------------------------------------------------------ import sys if sys.version_info[0] < 3:     _self_attr = 'im_self' else:     _self_attr = '__self__' class methodwrapper(object):     def __init__(self, descriptor, callable=None):         self.descriptor = descriptor         self.callable = callable     def __get__(self, obj, type=None):         if self.callable is not None:             return self         return methodwrapper(self.descriptor,                              self.descriptor.__get__(obj, type))     def __call__(self, *args, **kwargs):         if self.callable is None:             raise TypeError('wrapper called before __get__')         try:             obj = getattr(self.callable, _self_attr)         except AttributeError:             # must be a function             return self.callable.__call__('inserted',                                           *args,                                           **kwargs)         if obj is None:             # must be an unbound method             if not args:                 raise TypeError('instance argument missing')             return self.callable.__call__(args[0],                                           'inserted',                                           *args[1:],                                           **kwargs)         # must be a bound method         return self.callable.__call__('inserted',                                       *args,                                       **kwargs) if __name__ == '__main__':     class A(object):         @methodwrapper         @staticmethod         def s(inserted):             return inserted         @methodwrapper         @classmethod         def c(cls, inserted):             return inserted         @methodwrapper         def i(self, inserted):             return inserted     a = A()     assert a.s() == 'inserted'     # instance binding - static method     assert a.c() == 'inserted'     # instance binding - class method     assert a.i() == 'inserted'     # instance binding - instance method     assert A.s() == 'inserted'     # class binding - static method     assert A.c() == 'inserted'     # class binding - class method     assert A.i(a) == 'inserted'    # class binding - instance method ------------------------------------------------------------ Exhibit B: Run wraptest.py in Python 2.5, and all assertions pass. However, run it in Python 3.0, and witness: Traceback (most recent call last):   File "wraptest.py", line 64, in      assert A.i(a) == 'inserted'    # class binding - instance method AssertionError ------------------------------------------------------------ Summary: There is a subtle difference between an unbound method and a function.  Generally, one can assume that the underlying function of an unbound method will expect an instance as the first argument, but this is not the case for plain functions.  Here's why: 1) The staticmethod descriptor always returns a function, to which no arguments are passed implicitly. 2) The classmethod descriptor always returns a bound method, which implicitly passes an instance (the class) as the first argument to the underlying function. 3) That leaves regular instance methods.  In version 2.5, the descriptor will return either a bound method (in which the instance argument is passed implicitly), or an unbound method (in which an instance argument must be passed explicitly), depending on the binding used: instance binding or class binding, respectively.  Either way, the underlying function will expect an instance as the first argument. Here is a table of combinations for Python 2.5, using wraptest.py as the template: a.s -> staticmethod descriptor -> function (no im_self attribute) a.c -> classmethod descriptor -> bound method (im_self = A) a.i -> function descriptor -> bound method (im_self = a) A.s -> staticmethod descriptor -> function (no im_self attribute) A.c -> classmethod descriptor -> bound method (im_self = A) A.i -> function descriptor -> unbound method (im_self = None) If you are creating a custom descriptor that needs to wrap static methods, class methods, and instance methods, one can determine the difference between instance binding and class binding for instance methods by whether the descriptor returns a bound method or an unbound method. However, in 3.0, unbound methods have been done away with, and the situation is as follows: a.s -> staticmethod descriptor -> function (no __self__ attribute) a.c -> classmethod descriptor -> bound method (__self__ = A) a.i -> function descriptor -> bound method (__self__ = a) A.s -> staticmethod descriptor -> function (no __self__ attribute) A.c -> classmethod descriptor -> bound method (__self__ = A) A.i -> function descriptor -> **function!** (no __self__ attribute) As such, if the wrapper receives a function from the descriptor, how does it know if it is a static method, which doesn't need an instance argument, or an instance method with class binding, which does? OK, I suppose that that the code *could* check if the descriptor is an instance of staticmethod or classmethod.  However, this is slower than the duck typing used in the example code, assuming that a majority of methods are plain instance methods.  And, by using duck typing, the class can wrap other descriptors that might mimic classmethod or staticmethod.  The code above could even be extended to do just that, by mimicking the behavior of the underlying descriptor (essentially masking its presence).   That way, several wrappers could be chained together, without having to check the type of the descriptor. Another solution would be to create two separate wrappers: one for static methods, and one for class methods and instance methods. However, this seems clumsy, since it isn't even necessary in 2.5. Have I made a case for the existence of unbound methods?  I don't know.  I'll be the first to admit that I may have missed something vitally important in my analysis.  Perhaps there is a new way of doing things in 3.0 to which I should strive.  Or, perhaps I am too hung up on creating a "universal" method wrapper. Anyway, those are my thoughts on the subject.  Thanks for your time, Anthony Tolle _______________________________________________ Python-3000 mailing list smsbmo-9552@python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/python-3000%40tangomu.com