[Python-3000] Bound and unbound methods

Nick Coghlan ncoghlan at gmail.com
Fri Aug 18 17:14:16 CEST 2006


Guido van Rossum wrote:
>> Would a possible special method name __methodcall__ be accepted, where
>> if it exists on a callable, you can expect to use it as __call__ but
>> with the understanding that it accepts <expr> as self when called in
>> an optimizable form? This would reduce the method call to two
>> attribute lookups before the call instead of an instansiation and all
>> the heavy lifting currently done. For normal functions,
>> 'f.__methodcall__ is f.__call__' may be true, but the existance of
>> that __methodcall__ name just gives you an extra contract.
> 
> I'd like to answer "no" (since I think this whole idea is not a very
> fruitful avenue) but frankly, I have no idea what you are trying to
> describe. Are you even aware of the descriptor protocol (__get__) and
> how it's used to create a bound method (or something else)?
> 
> No reply is needed.

If I understand Calvin right, the best speed up we could get for the status 
quo is for the "METHOD_CALL" opcode to:
   1. Do a lookup that bypasses the descriptor machinery (i.e. any __get__ 
methods are not called at this point)
   2. If the object is a function object, invoke __call__ directly, supplying 
the instance as the first argument
   3. If the object is a classmethod object, invoke __call__ directly, 
supplying the class as the first argument
   4. If the object is a staticmethod object, invoke __call__ directly, 
without supplying any extra arguments
   5. If the object has a __get__ method, call it and invoke __call__ on the 
result
   6. Otherwise, invoke __call__ on the object

(Caveat: this omits details of the lookup process regarding how descriptors 
are handled that an actual implementation would need to deal with).

I think what Calvin is suggesting is, instead of embedding all those special 
cases in the op code, allow a descriptor to define __methodcall__ as an 
optimised combination of calling __get__ and then invoking __call__ on the 
result. Then the sequence of events in the op code would be to:

   1. Do a lookup that bypasses the descriptor machinery
   2. If the object defines it, invoke __methodcall__ directly, supplying the 
instance as the first argument and the class as the second argument (similar 
to __get__), followed by the args tuple as the 3rd argument and the keyword 
dictionary as the 4th argument.
   5. If the object doesn't define __methodcall__, but has a __get__ method, 
then call it and invoke __call__ on the result
   6. Otherwise, invoke __call__ on the returned object

For example, on a function object, __methodcall__ would look like:

   def __methodcall__(self, obj, cls, args, kwds):
       if obj is None:
           raise TypeError("Cannot call unbound method")
       return self(obj, *args, **kwds)

On a class method descriptor:

   def __methodcall__(self, obj, cls, args, kwds):
       return self._function(cls, *args, **kwds)

On a static method descriptor:

   def __methodcall__(self, obj, cls, args, kwds):
       return self._function(*args, **kwds)

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://www.boredomandlaziness.org


More information about the Python-3000 mailing list