2011-05-15 3 views
4

Вдохновленный Мухаммадом Алкарури ответил в What are good uses for Python3's "Function Annotations", я хочу это сделать multimethod для методов, а не регулярных функций. Однако, когда я делаю этоМетод украшения (перегрузка методов класса)

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! 
     function = self.typemap.get(types) 
    if function is None: 
     raise TypeError("no match") 
    return function(*args) 
def register(self, types, function): 
    if types in self.typemap: 
     raise TypeError("duplicate registration") 
    self.typemap[types] = function 

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

class A: 
@multimethod 
def foo(self, a: int): 
    return "an int" 

a = A() 
print(a.foo(1)) 

Я получил это:

Traceback (most recent call last): 
    File "test.py", line 33, in <module> 
    print(a.foo(1)) 
    File "test.py", line 12, in __call__ 
    return function(*args) 
TypeError: foo() takes exactly 2 arguments (1 given) 

который, кажется, можно ожидать, как объяснено в Decorating a method, из-за self аргумента.

Но я не знаю, как заставить его работать. Ну, когда я удаляю «я», он работает (почти) отлично, но я не хочу его удалять. Обратите внимание, что я делаю это для практики, я знаю, что есть некоторые библиотеки, обеспечивающие перегрузку метода.

Что я пробовал:

  • очень глупо, но хотел бы попробовать, - добавил параметр self в def multimethod(function) - та же ошибка

  • Я думал о добавлении в __init__ из class MultiMethod третьего параметра - obj и сохранен self как член, но я не могу сделать это через multimethod, поскольку это функция.

  • Я не хочу, чтобы добавить параметры для декоратора, поэтому эти опции (если вообще возможно) игнорируется

Я прочитал несколько подобных вопросов, но не нашли то, что я искал для. Я почти уверен, что это фиктивный вопрос, но у меня кончились идеи.

ответ

5

Основная проблема заключается в том, что вы используете класс вместо функции. Механизм привязки этого класса к экземпляру, из которого он вызван, не существует, в отличие от функции, где это происходит автоматически.

Короче говоря, когда вы делаете a.foo(..), он возвращает MultiMethod, но этот объект не имеет понятия, что он должен быть связан с a.

Вы должны в той или иной форме передать этот экземпляр. Один простой способ, чтобы обернуть все это в функции, и пусть Python делать это дело:

registry = {} 

class MultiMethod(object): 
    def __init__(self, name): 
     self.name = name 
     self.typemap = {} 

    # self = a MultiMethod instance, instance = the object we want to bind to 
    def __call__(self, instance, *args): 
     types = tuple(arg.__class__ for arg in args) # a generator expression! 
     function = self.typemap.get(types) 

     if function is None: 
      raise TypeError("no match") 
     return function(instance, *args) 

    def register(self, types, function): 
     if types in self.typemap: 
      raise TypeError("duplicate registration") 
     self.typemap[types] = function 

def multimethod(function): 
    name = function.__name__ 
    mm = registry.get(name) 
    if mm is None: 
     mm = registry[name] = MultiMethod(name) 

    types = tuple(function.__annotations__.values()) 
    mm.register(types, function) 
    # return a function instead of a object - Python binds this automatically 
    def getter(instance, *args, **kwargs): 
     return mm(instance, *args, **kwargs) 
    return getter 

class A: 
    @multimethod 
    def foo(self, a: int): 
     return "an int", a 

a = A() 
print(a.foo(1)) 

Более сложный способ будет написать свой собственный дескриптор на A класса, который делает это связывание.

+0

Haaa, спасибо, это действительно имеет смысл. +1 и принято. –

Смежные вопросы