Would you mind giving an "executive summary" of your argument that doesn't require scanning 40 lines of code?
On Thu, Mar 6, 2008 at 7:43 PM, Anthony Tolle <[EMAIL PROTECTED]> wrote: > 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 <module> > 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 > Python-3000@python.org > http://mail.python.org/mailman/listinfo/python-3000 > Unsubscribe: > http://mail.python.org/mailman/options/python-3000/guido%40python.org > -- --Guido van Rossum (home page: http://www.python.org/~guido/) _______________________________________________ Python-3000 mailing list Python-3000@python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/archive%40mail-archive.com