[pypy-dev] Multimethods again

Armin Rigo arigo at tunes.org
Tue Feb 17 22:58:58 CET 2004


Hello everybody,

I just found out a neat way to do simple multimethods in Python, so I thought 
I'd share it with you :-)

The following is only for dispatching over two arguments.  The idea is to use 
a pair(a,b) object that is just like a tuple (a,b), but with methods.  Any 
(regular Python) method that you put in the class of pair(a,b) works like a 
multimethod dispatching on both a and b.  The metaclass hack is only a 
syntactic convenience.

I don't know if this is relevant to PyPy.  It's probably one of the shortest 
multimethod implementations for Python, whereas PyPy's is probably one of the 
longest :-)

Armin

# ______________________________________________

class extendabletype(type):
    """A type with a syntax trick: 'class __extend__(t)' actually extends
    the definition of 't' instead of creating a new subclass."""
    
    def __new__(cls, name, bases, dict):
        if name == '__extend__':
            return bases[0]   # return existing base
        else:
            return type.__new__(cls, name, bases, dict)
    
    def __init__(self, name, bases, dict):
        if name == '__extend__':
            for key, value in dict.items():
                setattr(self, key, value)


def pair(a, b):
    """Return a pair object."""
    tp = typeofpair(a.__class__, b.__class__)
    return tp((a, b))   # tp is a subclass of tuple

pairtypecache = {}

def typeofpair(cls1, cls2):
    """type(pair(a,b)) is typeofpair(a.__class__, b.__class__)."""
    try:
        pair = pairtypecache[cls1, cls2]
    except KeyError:
        name = 'typeofpair(%s, %s)' % (cls1.__name__, cls2.__name__)
        bases1 = [typeofpair(base1, cls2) for base1 in cls1.__bases__]
        bases2 = [typeofpair(cls1, base2) for base2 in cls2.__bases__]
        bases = tuple(bases1 + bases2) or (tuple,)  # 'tuple': ultimate base
        pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})
    return pair

# ____________________________________________________________


class A(object):
    pass

class B(object):
    pass

class C(A):
    pass

class D(B):
    pass

a = A()
b = B()
c = C()
d = D()
assert isinstance(pair(a,b), typeofpair(A, B))


class __extend__(typeofpair(A, B)):
    
    def add((a, b)):
        print "a+b"
        
    def sub((a, b)):
        print "a-b"


class __extend__(typeofpair(C, B)):

    def add((c, b)):
        print "c+b"


# alternate less magic syntax
def sub((a, d)):
    print "a-d"
typeofpair(A, D).sub = sub


pair(a,b).add()
pair(a,b).sub()
pair(c,d).add()
pair(c,d).sub()



More information about the Pypy-dev mailing list