开发者

Dynamic dispatch and inheritance in python

I'm trying to modify Guido's multimethod (dynamic dispatch code):

http://www.artima.com/weblogs/viewpost.jsp?thread=101605

to handle inheritance and possibly out of order arguments.

e.g. (inheritance problem)

class A(object):
  pass

class B(A):
  pass

@multimethod(A,A)
def foo(arg1,arg2):
  print 'works'


foo(A(),A()) #works

foo(A(),B()) #fails

Is there a better way than iteratively checking for the super() of each item until one is found?

e.g. (argument ordering problem) I was thinking of this from a collision detection standpoint.

e.g.

foo(Car(),Truck()) and
foo(Truck(), Car()) and

should both trigger

foo(Car,Truck) # Note: @multimethod(Truck,Car) will throw an exception if @multimethod(Car,Truck) was registered first?

I'm looking specifically for an 'elegant' solution. I know that I could just br开发者_如何学Cute force my way through all the possibilities, but I'm trying to avoid that. I just wanted to get some input/ideas before sitting down and pounding out a solution.

Thanks


Regarding the inheritance issue: This can be done with a slight change to MultiMethod. (Iterating through self.typemap and checking with issubclass):

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        for typemap_types in self.typemap:
            if all(issubclass(arg_type,known_type)
                   for arg_type,known_type in zip(types,typemap_types)):
                function = self.typemap.get(typemap_types)
                return function(*args)
        raise TypeError("no match")
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(*types):
    def register(function):
        name = function.__name__
        mm = registry.get(name)
        if mm is None:
            mm = registry[name] = MultiMethod(name)
        mm.register(types, function)
        return mm
    return register

class A(object):
  pass

class B(A):
    pass

class C(object):
    pass

@multimethod(A,A)
def foo(arg1,arg2):
  print 'works'


foo(A(),A()) #works

foo(A(),B()) #works

foo(C(),B()) #raises TypeError

Note that self.typemap is a dict, and dicts are unordered. So if you use @multimethod to register two functions, one whose types are subclasses of the other, then the behavior of foo may be undefined. That is, the result would depend on which typemap_types comes up first in the loop for typemap_types in self.typemap.


super() returns a proxy object, not the parent class (because you can have multiple inheritance), so that wouldn't work. Using isinstance() is your best bet, although there's no way to make it as elegant as the dictionary lookups using type(arg).

I don't think allowing alternative argument orderings is a good idea; it's liable to lead to nasty surprises, and making it compatible with inheritance as well would be a significant headache. However, it would be quite simple to make a second decorator for "use this function if all the arguments are of type A", or "use this function if all the arguments are in types {A, B, E}".

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜