Ссылаясь на чисто виртуальный метод - PullRequest
1 голос
/ 13 марта 2019

Я, наверное, здесь упускаю что-то очевидное. У меня есть простая иерархия классов, которая выглядит примерно так:

class Any:
    def op2_additive_add(self, other: 'Any') -> 'Any':
        raise NotImplementedError

    def op2_multiplicative_multiply(self, other: 'Any') -> 'Any':
        raise NotImplementedError

    def op2_exponential_power(self, other: 'Any') -> 'Any':
        raise NotImplementedError

    # A dozen of similar methods not shown

class Rational(Any):
    def op2_additive_add(self, other: 'Any') -> 'Rational':
        pass    # Implementation not shown

    def op2_multiplicative_multiply(self, other: 'Any') -> 'Rational':
        pass    # Implementation not shown

    def op2_exponential_power(self, other: 'Any') -> 'Rational':
        pass    # Implementation not shown

class Set(Any):
    def op2_additive_add(self, other: 'Any') -> 'Set':
        pass    # Implementation not shown

    def op2_multiplicative_multiply(self, other: 'Any') -> 'Set':
        pass    # Implementation not shown

    def op2_exponential_power(self, other: 'Any') -> 'Set':
        pass    # Implementation not shown

# Half a dozen of similar derived types not shown.

Я реализую класс диспетчера, который должен выбирать запрошенную операцию с двумя операндами, не зная их типов, и затем передавать ссылку на правильный метод исполнителю. Примерно так (код ниже не будет работать):

def select_operator(selector) -> typing.Callable[[Any, Any], Any]:
    if selector.such_and_such():
        return Any.op2_additive_add
    elif selector.so_and_so():
        return Any.op2_exponential_power
    # And so on

Приведенный выше код не будет работать, потому что попытка вызвать возвращенный несвязанный метод обойдет динамическую диспетчеризацию; то есть select_operator(selector)(foo, bar) всегда будет выбрасывать NotImplementedError.

Лучшее решение, которое я мог бы найти до сих пор, примерно так, и это не очень красиво:

def select_operator(selector) -> str:
    if selector.such_and_such():
        return Any.op2_additive_add.__name__
    elif selector.so_and_so():
        return Any.op2_exponential_power.__name__
    # And so on

method_name = select_operator(selector)
getattr(foo, method_name)(bar)

TL; DR: как отложить процесс динамической отправки до тех пор, пока я не получу ссылку на метод?

1 Ответ

1 голос
/ 13 марта 2019

Может быть, это будет более подходящим?

class Any:
    def select_and_do_op(self, selector, other):
        if selector.such_and_such():
            return self.op2_additive_add.(other)
        elif selector.so_and_so():
            return self.op2_exponential_power(other)
    ...

foo.select_and_do_op(selector, bar)

UPDATE

Еще одно решение:

def select_operator(selector):
    if selector.such_and_such():
        return lambda foo, bar: foo.op2_additive_add(bar)
    elif selector.so_and_so():
        return lambda foo, bar: foo.op2_exponential_power(bar)
    ...

operator = select_operator(selector)
result = operator(foo, bar)
...