2011-01-09 1 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 

Есть ли лучший способ, чем итеративна проверка супер() каждый элемент до одного найдено?

например. (проблема упорядочения аргументов) Я думал об этом с точки зрения обнаружения столкновения.

например.

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-Rules, который представляет собой еще одну реализацию общей отправки в python, которая может обрабатывать наследование. http://pypi.python.org/pypi/PEAK-Rules – albertov

ответ

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 является ДИКТ и dicts является неупорядоченной. Поэтому, если вы используете @multimethod для регистрации двух функций, один из которых является подклассами другого, то поведение foo может быть неопределенным. То есть результат будет зависеть от того, какой из typemap_types появляется первым в цикле for typemap_types in self.typemap.