问题 在Python中将“指向虚函数的指针”作为参数传递


比较以下代码 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);
}

并在 蟒蛇

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)

C ++ 代码将打印:

one
two

蟒蛇 代码将打印

one
one

如何传递“指向方法的指针”并将其解析为被覆盖的方法,在Python中实现与C ++相同的行为?

添加一些上下文并解释为什么我最初考虑这种模式。我有一个由可以子类化的节点组成的树。我想创建一个通用的图遍历函数,它接受图的节点以及可能在图节点的子类中被覆盖的函数。给定为相邻节点计算的值,该函数计算节点的某个值。目标是返回为给定节点计算的值(需要遍历整个图形)。


2733
2017-08-05 01:43


起源

你考虑过构建Python代码吗? 不 试图用不同的语言复制语义?我觉得更多Pythonic的做法与继承或动态调度无关。 - uh oh somebody needs a pupper
我同意,问题在于我的问题没有更好的模式。请参阅编辑以获取问题描述。我更喜欢Pythonic解决方案。 - Andrzej Pronobis
听起来你想要访客模式。 - uh oh somebody needs a pupper
您是否可以发布一个(简化的)示例,说明如何在Python中处理它? - Andrzej Pronobis


答案:


关于你的编辑,你可以做的一件事是使用一个小的包装器lambda来调用你想要引用的方法。这样,方法调用看起来像“常规python代码”,而不是基于基于字符串的访问的复杂事物。

在您的示例中,唯一需要更改的部分是对调用 test 功能:

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

9
2017-08-05 03:56



这似乎是一种更清洁,Pythonic解决问题的方法。但是,它不允许动态调度,可以用另一个答案模拟(如果需要的话)。 - Andrzej Pronobis
我不明白你的意思。该 bar 方法调用将被动态调度。 - hugomg
我的术语可能已关闭。我的意思是,采用这种方法,不可能处理这种情况 bar 采取不同的论点 A 并在 B例如,在 B 它需要一个额外的论点。在字符串方法中, test 可以选择应用不同的参数 bar 取决于的类型 obj。 - Andrzej Pronobis
使A和B采用不同的参数会很奇怪(有一个共同的方法就是共享一个接口......)但是没有什么可以阻止测试来检查objs列表中对象的类型。唯一的问题是如果A和B采用不同数量的参数,但你可以通过将lambda变为vararg函数来解决这个问题: lambda x, *args : x.bar(*args) - hugomg
忘了关于varargs,谢谢! - Andrzej Pronobis


以下产生您想要的输出:

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")

4
2017-08-05 02:08



这从字面上回答了这个问题,但我认为它根本不代表OP正在努力实现的目标。当然,这个程序不会在更复杂的例子中扩展。 - uh oh somebody needs a pupper
@uh哦:IMO它回答了这个问题并代表了OP试图完成的事情,因为Python没有指针。此外,它应该“缩放”以及C ++版本。 - martineau
这是我的观点。 Python没有指针或多个调度,所以我希望有一种更惯用的方法来实现相同的功能 目的。所有这一切都强化了错误的做事方式。 - uh oh somebody needs a pupper
@uh哦:除了你,谁说过多次派遣?我的回答中的代码与继承一起使用,并显示了引用实例的变量属性的惯用方法。随意发布您自己的答案,即使它是“你不能用Python做到这一点”。 - martineau
谢谢你的回答。我认为它确实回答了我原来的问题。我想到了一个类似的解决方案,但我希望有更清洁/更好的解决方案。如果没有,我建议扩展这个答案,“这不能用Python做任何其他方式:”,我也很乐意听到关于我的问题使用的最佳模式的意见(见编辑)。 - Andrzej Pronobis