On Thu, Mar 6, 2008 at 11:59 PM, Guido van Rossum <[EMAIL PROTECTED]> wrote: > Would you mind giving an "executive summary" of your argument that > doesn't require scanning 40 lines of code? >
Let me put it this way: if unbound methods are gone for good, then I think it would nice to develop some guidance on checking the signature of callable objects, to enable decorators to play nice with each other--especially if they intend to modify the argument list. For example, let's say there are two wrappers around two different method types, like so (this is a short pseudo-code example, I promise): class A(object): @wrapper2 @wrapper1 @staticmethod def sm(*args): ... @wrapper2 @wrapper1 def im(self, *args): ... Let's say the purpose of both wrapper1 and wrapper2 is to insert a new argument into the argument list. How should they do this for each combination of method and binding type? It would work as follows (in these examples, self.callable is what was returned by the __get__ method of the wrapped descriptor): For a static method: return self.callable(newarg, *args, **kwargs) For an instance method with instance binding (i.e. a bound method): return self.callable(newarg, *args, **kwargs) For an instance method with class binding (i.e. an unbound method): return self.callable(args[0], newarg, args[1:], **kwargs) Notice the change? This is necessary to maintain consistency for handling instance binding and class binding. With instance binding (i.e. a bound method), the instance argument is passed implicitly--as the first argument--by the wrapped callable. Thus, the wrapper must simulate this for class binding, because the instance argument must be passed explicitly. So how does wrapper1 know whether it is wrapping a static method, a bound method, or an unbound method? Well, one way it could do this is to examine the type of the descriptor it is wrapping. If it sees that it is an instance of staticmethod, then it can process it as such. However, wrapper2 doesn't have this luxury, since all it knows is that it is wrapping an instance of wrapper1. The other way is to examine the callable returned by the __get__ method of the wrapped descriptor. This is where things get interesting. In Python 2.5, this was extremely easy: If the callable has no im_self attribute, then it is a static function. If the callable has an im_self attribute, and im_self is None, then it is an unbound method. If im_self is not None, then it is a bound method. Simple. But there's more: because the attribute signature is so simple, it can easily be emulated by wrapper1's __get__ function, and thus wrapper2 would be able to make the same determination. Plus, wrapper1 could even copy the im_func attribute, so it would be possible to follow the chain of wrappers all the way back to the original function. But Python 3.0 breaks the pattern, because unbound methods no longer exist. When examining the callable returned by __get__, the signature for a static method and an "unbound" method is the same (both plain functions). Now the wrapper will not be able to determine how to modify the arguments. How about this as a compromise? 1) In Python 3.0, all functions will have a read-only __self__ attribute that is set to None (so that it looks like an unbound method, signature wise). 2) Bound methods will continue to behave as before; as a callable object with a __self__ attribute referencing the instance object, and a __func__ attribute referencing the original function object. 3) If a function is decorated with @staticmethod, then its __get__ method will return a callable object that wraps the function. The callable object will have no __self__ attribute, but it will have a __func__ attribute referencing the original function. 4) As a convention, a callable that wraps another callable will copy the __self__ attribute (or lack thereof), and the __func__ attribute. Thus, the signature will be preserved up the chain. Thus, the signature for a callable can be tested as follows: if hasattr(obj, '__self__'): if obj.__self__ is None: ..."unbound" method... else: ....bound method... else: ....static method.... Note that class methods always fall into the category of a bound method, whether or not instance binding is used. If there is a need to differentiate between an instance method and a class method, then the __self__ object can be examined to see if it a class. _______________________________________________ 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