[Python-Dev] Classes and Metaclasses in Smalltalk

M.-A. Lemburg mal@lemburg.com
Wed, 02 May 2001 16:04:29 +0200


Here's an implementation of what I currently use to track down
the basemethod (taken from mx.Tools):

import types
_basemethod_cache = {}

def basemethod(object,method=None,

               cache=_basemethod_cache,InstanceType=types.InstanceType,
               ClassType=types.ClassType,None=None):

    """ Return the unbound method that is defined *after* method in the
        inheritance order of object with the same name as method
        (usually called base method or overridden method).

        object can be an instance, class or bound method. method, if
        given, may be a bound or unbound method. If it is not given,
        object must be bound method.

        Note: Unbound methods must be called with an instance as first
        argument.

        The function uses a cache to speed up processing. Changes done
        to the class structure after the first hit will not be noticed
        by the function.

        XXX Rewrite in C to increase performance.

    """
    if method is None:
        method = object
        object = method.im_self
    defclass = method.im_class
    name = method.__name__
    if type(object) is InstanceType:
        objclass = object.__class__
    elif type(object) is ClassType:
        objclass = object
    else:
        objclass = object.im_class

    # Check cache
    cacheentry = (defclass, name)
    basemethod = cache.get(cacheentry, None)
    if basemethod is not None:
        if not issubclass(objclass, basemethod.im_class):
            if __debug__:
                sys.stderr.write(
                    'basemethod(%s, %s): cached version (%s) mismatch: '
                    '%s !-> %s\n' %
                    (object, method, basemethod,
                     objclass, basemethod.im_class))
        else:
            return basemethod

    # Find defining class
    path = [objclass]
    while 1:
        if not path:
            raise AttributeError,method
        c = path[0]
        del path[0]
        if c.__bases__:
            # Prepend bases of the class
            path[0:0] = list(c.__bases__)
        if c is defclass:
            # Found (first occurance of) defining class in inheritance
            # graph
            break
        
    # Scan rest of path for the next occurance of a method with the
    # same name
    while 1:
        if not path:
            raise AttributeError,name
        c = path[0]
        basemethod = getattr(c, name, None)
        if basemethod is not None:
            # Found; store in cache and return
            cache[cacheentry] = basemethod
            return basemethod
        del path[0]
    raise AttributeError,'method %s' % name
    
-- 
Marc-Andre Lemburg
______________________________________________________________________
Company & Consulting:                           http://www.egenix.com/
Python Software:                        http://www.lemburg.com/python/