Python and generic programming

Delaney, Timothy C (Timothy) tdelaney at avaya.com
Mon Nov 22 23:56:34 EST 2004


And now a version that works with multiple arguments ...

    import itertools

    def generic (genfunc):
        speclist = []

        def wrapf (*args):
            typs = tuple(type(arg) for arg in args if not
isinstance(arg, type))
            assert len(args) == len(typs)

            for specfunc, specs, typs in speclist:
                if len(args) != len(typs):
                    continue

                for arg, spec, typ in itertools.izip(args, specs, typs):
                    if isinstance(spec, type):
                        if not isinstance(arg, typ):
                            break
                    elif (arg != spec) or (not isinstance(arg, typ)):
                        break
                else:
                    return specfunc(*args)

            return genfunc(*args)

        wrapf.wrappedfunc = genfunc
        wrapf.speclist = speclist
        wrapf.func_name = genfunc.func_name

        return wrapf

    def specialised (genf, *specifiers):
        try:
            genf = genf.genfunc
        except AttributeError:
            pass

        assert len(specifiers) == genf.wrappedfunc.func_code.co_argcount

        def spd(specf):

            assert len(specifiers) == specf.func_code.co_argcount

            typs = []

            for s in specifiers:
                if isinstance(s, type):
                    typs.append(s)
                else:
                    typs.append(type(s))

            typs = tuple(typs)

            genf.speclist[:0] = [(specf, specifiers, typs)]
            return specf

        spd.genfunc = genf
        return spd

    # For my US friends ;)
    specialized = specialised

    @generic
    def some_function (arg):
        print "generic - %r" % (arg,)

    @specialised(some_function, int)
    @specialised(some_function, long)
    def some_specific_function(arg):
        print "int-specific - %r" % (arg,)

    @specialised(some_function, float)
    def some_specific_function(arg):
        print "float-specific - %r" % (arg,)

    @specialised(some_function, 1)
    @specialised(some_function, 1.0)
    @specialised(some_function, 1L)
    def some_specific_function(arg):
        print "one-specific - %r" % (arg,)

    @generic
    def other_function (arg1, arg2):
        print "generic - %r, %r" % (arg1, arg2)

    @specialised(other_function, 1, object)
    def other_specific_function (arg1, arg2):
        print "1/object - %r, %r" % (arg1, arg2)

    @specialised(other_function, int, float)
    def other_specific_function (arg1, arg2):
        print "int/float - %r, %r" % (arg1, arg2)

    @specialised(other_function, 1, float)
    def other_specific_function (arg1, arg2):
        print "1/float - %r, %r" % (arg1, arg2)

    @specialised(other_function, 1, 2.0)
    def other_specific_function (arg1, arg2):
        print "1/2.0 - %r, %r" % (arg1, arg2)

    if __name__ == '__main__':
        some_function('abc')
        some_function(itertools)
        some_function(123)
        some_function(123L)
        some_function(123.0)
        some_function(1)
        some_function(1.0)
        some_function(1L)
        print
        other_function(2, 2)
        other_function(1, 2.0)
        other_function(1, 1.0)
        other_function(2, 2.0)
        other_function(1, 2)

In cases where more than one specialisation would match, the most
recently added will be used i.e. in the above, for other_function(1,
1.0) will use (int, float) even though (1, object) would also match. So
it's important to create functions in the correct order.

This is starting to get recipe-worthy - but I want to see what happens
with methods first ...

Tim Delaney



More information about the Python-list mailing list