2011-01-09 110 views
4

我试图修改圭多的多重方法(动态调度代码):动态分配和继承在Python

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

处理继承和可能失灵的论点。

例如(继承问题)

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 

是否有更好的方法比迭代检查每个项目的super()直到找到一个?

例如(参数排序问题) 我从碰撞检测的角度考虑了这个问题。

例如

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

都应该引起

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

我一个 '优雅' 的解决方案专找。我知道我可以通过所有可能性蛮横的力量,但我试图避免这种情况。我只想在坐下并敲出解决方案之前获得一些意见和建议。

感谢

+0

您可能想看看PEAK规则,这是python中可以处理继承的另一个通用调度实现。 http://pypi.python.org/pypi/PEAK-Rules – albertov 2011-01-09 19:53:30

回答

2

super()返回代理对象,而不是父类(因为你可以有多重继承),这样是行不通的。使用isinstance()是你最好的选择,尽管没有办法像使用type(arg)的词典查找那样使用它。

我不认为允许替代论证排序是一个好主意;它很容易导致令人讨厌的惊喜,并使其与遗传兼容,这将是一个令人头痛的问题。但是,如果为“如果所有参数都是A类型都使用此函数”,或者“如果所有参数的类型都是{A,B,E}”,则使用此函数将会非常简单。

2

关于继承问题:这可以通过对MultiMethod的轻微更改来完成。 (通过迭代self.typemap并用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 

注意self.typemap是一个字典,和类型的字典是无序的。因此,如果使用@multimethod来注册两个函数,一个的类型是另一个的子类,那么foo的行为可能是未定义的。也就是说,结果将取决于哪个typemap_types在循环for typemap_types in self.typemap中首先出现。