2016-08-05 14 views
11

Porównaj następujący kod w C++:Przechodząc "wskaźnik do funkcji wirtualnego" jako argument w Pythonie

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

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

Kod C++ wydrukuje:

one 
two 

natomiast Python kod wypisze

one 
one 

Jak mogę przekazać „wskaźnik do metody” i rozwiązać go zastąpionej jeden, osiągnięcie takiego samego zachowania w Pythonie jak w C++?

Aby dodać kontekst i wyjaśnić, dlaczego początkowo pomyślałem o tym wzorze. Mam drzewo składające się z węzłów, które mogą być podklasowane. Chciałbym utworzyć ogólną funkcję przemierzania wykresu, która pobiera węzeł wykresu, a także funkcję, która może być nadpisana w podklasach węzłów grafów. Funkcja oblicza pewną wartość dla węzła, biorąc pod uwagę wartości obliczone dla sąsiednich węzłów. Celem jest zwrócenie wartości obliczonej dla danego węzła (co wymaga przejścia całego wykresu).

+0

czy za strukturyzacji kodu Pythona tak, że jesteś * nie * próbuje replikować w semantykę inny język? Czuję, że im bardziej Pythonic to robi, nie ma nic wspólnego z dziedziczeniem ani dynamiczną wysyłką. –

+0

Zgadzam się, problem polega na tym, że nie mam lepszego wzoru na mój problem. Zobacz opis opisu problemu. Wolałbym rozwiązanie Pythoniczne. –

+0

Wygląda na to, że chcesz uzyskać wzór gościa. –

Odpowiedz

9

dotycząceTwojego edycji, jedną rzeczą, jaką można zrobić, to użyć trochę otoki lambda, który wywołuje metodę chcesz odwołać. W ten sposób wywołanie metody wygląda jak "zwykły kod Pythona", zamiast być czymś skomplikowanym w oparciu o dostęp oparty na łańcuchach.

W przykładzie jedyną częścią, która musiałaby zmienić to wywołanie test funkcję:

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

Wydaje się to być czystszym, Pythonicznym sposobem rozwiązania problemu. Nie pozwala jednak na dynamiczną wysyłkę, co można emulować drugą odpowiedzią (jeśli jest taka potrzeba). –

+0

Nie rozumiem, co masz na myśli. Wywołanie metody "bar" zostanie dynamicznie wywołane. – hugomg

+0

Moja terminologia może być wyłączona. Chodziło mi o to, że przy takim podejściu nie można obsłużyć przypadku, w którym 'bar' przyjmuje różne argumenty w' A' i 'B', na przykład w' B' przyjmuje dodatkowy argument. W podejściu łańcuchowym 'test' może wybrać różne argumenty' bar' w zależności od typu 'obj'. –

4

Poniższa produkuje wyjście chcesz:

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

To odpowiada na pytanie dosłownie, ale nie sądzę, że w ogóle reprezentuje to, co OP próbuje osiągnąć. Z pewnością ten program nie byłby skalowalny w bardziej złożonym przykładzie. –

+0

@uh oh: IMO odpowiada na pytanie i reprezentuje to, co OP próbuje osiągnąć, ponieważ Python nie ma wskaźników. Ponadto powinien "skalować" się tak samo, jak wersja C++. – martineau

+1

To był mój punkt widzenia. Python nie ma wskaźników ani wielokrotnej wysyłki, więc oczekiwałbym bardziej idiotycznego sposobu osiągnięcia tego samego * celu *. Wszystko to wzmacnia niewłaściwy sposób robienia rzeczy. –

Powiązane problemy