Передача "указателя на виртуальную функцию" в качестве аргумента в Python

Сравните следующий код в С++:

#include <iostream>
#include <vector>

struct A
{
  virtual void bar(void) { std::cout << "one" << std::endl; }
};

struct B : public A
{
  virtual void bar(void) { std::cout << "two" << std::endl; }
};

void test(std::vector<A*> objs, void (A::*fun)())
{
  for (auto o = objs.begin(); o != objs.end(); ++o)
  {
    A* obj = (*o);
    (obj->*fun)();
  }
}

int main()
{
  std::vector<A*> objs = {new A(), new B()};

  test(objs, &A::bar);
}

и в Python:

class A:

    def bar(self):
        print("one")


class B(A):

    def bar(self):
        print("two")


def test(objs, fun):
    for o in objs:
        fun(o)

objs = [A(), B()]
test(objs, A.bar)

Код С++ напечатает:

one
two

в то время как код Python напечатает

one
one

Как передать "указатель на метод" и разрешить его переопределенному, достигая того же поведения в Python, что и на С++?

Чтобы добавить некоторый контекст и объяснить, почему я изначально думал об этом шаблоне. У меня есть дерево, состоящее из узлов, которые могут быть подклассифицированы. Я хотел бы создать общую функцию обхода графа, которая принимает node графа, а также функцию, которая может быть переопределена в подклассах узлов графа. Функция вычисляет некоторое значение для node, заданных значений, вычисленных для соседних узлов. Цель состоит в том, чтобы вернуть значение, рассчитанное для данного node (что требует прохождения всего графика).

Ответ 1

Что касается вашего редактирования, то одна вещь, которую вы можете сделать, это использовать небольшую обертку лямбда, которая вызывает метод, который вы хотите ссылаться. Таким образом, вызов метода выглядит как "обычный код python" вместо того, чтобы быть чем-то сложным, основанным на строчном доступе.

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

test(objs, (lambda x: x.bar()))

Ответ 2

Ниже вы получите требуемый результат:

class A:
    def bar(self):
        print("one")

class B(A):
    def bar(self):
        print("two")

def test(objs, funcname):
    noop = lambda: None
    for o in objs:
        getattr(o, funcname, noop)()

objs = [A(), B()]
test(objs, "bar")