Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a "pointer to a virtual function" as argument in Python

Compare the following code in C++:

#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);
}

and in 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)

The C++ code will print:

one
two

while the Python code will print

one
one

How can I pass "a pointer to a method" and resolve it to the overridden one, achieving the same behavior in Python as in C++?

To add some context and explain why I initially thought about this pattern. I have a tree consisting of nodes that can be subclassed. I would like to create a generic graph traversal function which takes a node of the graph as well as a function which might be overridden in subclasses of graph nodes. The function calculates some value for a node, given values calculated for adjacent nodes. The goal is to return a value calculated for the given node (which requires traversing the whole graph).

like image 498
Andrzej Pronobis Avatar asked Aug 05 '16 01:08

Andrzej Pronobis


2 Answers

Regarding your edit, one thing you could do is use a little wrapper lambda that calls the method you want to reference. This way the method call looks like "regular python code" instead of being something complicated based on string-based access.

In your example, the only part that would need to change is the call to the test function:

test(objs, (lambda x: x.bar()))
like image 172
hugomg Avatar answered Oct 11 '22 05:10

hugomg


The following produces the output you want:

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")
like image 27
martineau Avatar answered Oct 11 '22 07:10

martineau