2014-09-17 11 views
8

qui è un'implementazione semplificata del pattern visitor in C++. È possibile implementare qualcosa di simile in Python?Modello visitatore in pitone

Ne ho bisogno, perché passerò gli oggetti dal codice C++ a una funzione in Python. La mia idea era di implementare un visitatore in Python per scoprire il tipo di oggetto.

mio codice C++:

#include <iostream> 
#include <string> 


class t_element_base 
{ 
public: 
    virtual void accept(class t_visitor &v) = 0; 
}; 


class t_element_deriv_one: public t_element_base 
{ 
public: 
    void accept(t_visitor &v); 

    std::string t_element_deriv_one_text() 
    { 
     return "t_element_deriv_one"; 
    } 
}; 


class t_element_deriv_two: public t_element_base 
{ 
public: 
    void accept(t_visitor &v); 

    std::string t_element_deriv_two_text() 
    { 
     return "t_element_deriv_one"; 
    } 
}; 


class t_visitor 
{ 
public: 
    void visit(t_element_deriv_one& e){ std::cout << e.t_element_deriv_one_text() << std::endl; } 
    void visit(t_element_deriv_two& e){ std::cout << e.t_element_deriv_two_text() << std::endl; } 
}; 


void t_element_deriv_one::accept(t_visitor &v) 
{ 
    v.visit(*this); 
} 

void t_element_deriv_two::accept(t_visitor &v) 
{ 
    v.visit(*this); 
} 


int 
main 
(
void 
) 
{ 
    t_element_base* list[] = 
    { 
     new t_element_deriv_one(), new t_element_deriv_two() 
    }; 
    t_visitor visitor; 

    for(int i = 0; i < 2; i++) 
     list[ i ]->accept(visitor); 
} 

risposta

12

Il modello visitatore può essere implementato in Python, lo uso per implementare un'interfaccia pulita tra il mio livello di dati e presentazione. Il livello dati può determinare l'ordine dei dati. e il livello di presentazione semplicemente stampe/formati:

Nel mio modulo di dati da me:

class visited(object): 
    .... 
    def accept(self, visitor): 
     visitor.visit(self) 
     for child in self.children(): 
      child.accept(visitor) 

class typeA(visited): 
    .... 

Tutte le mie classi di dati ereditano da questa visita di classe e la classe visitato espone anche alcune funzioni semplici per la base i dati di cui tutti i miei oggetti hanno bisogno, ad es nome, genitore, ecc. e metodi per la gestione dell'elenco secondario, che è esposto dal metodo children() utilizzato in precedenza. ciascuna sottoclasse costruisce i propri dati, ha le proprie proprietà e forse anche la propria classe figlio - che viene aggiunta all'elenco dei bambini mantenuta dalla super classe visitata.

La mia classe visitatore è come questo:

class visitor(object): 
     def __init__(self, obj_id): 
      data_obj = _find_data_instance(obj_id) 
      data_obj.accept(self) 

     def visit(self, data_obj): 
      if isinstance(data_obj, typeA): 
       self.visit_typeA(dataobj) 

     def visit_typeA(self, dataobj): 
      """Formats the data for typeA""" 
      ... 

il _find_data_instance è un codice che costruisce o trova un istanza di una delle mie istanze di dati. Nel mio caso tutte le mie classi di dati hanno un costruttore che prende un objectId e restituisce, e l'oggetto visitatore sa quale classe di dati utilizzare.

+0

Grazie per il vostro aiuto. – bogdan

+0

Non so quale versione di Python usi, ma nella versione 2.7.6 la chiamata 'instance' non esiste. Dovresti usare 'isinstance' – Matthias

+0

@Matthias - buona presa - sarebbe stato felice che tu lo inserissi come suggerimento di modifica –

3

Si potrebbe implementare questo in Python, ma non c'è davvero alcun bisogno di. Python è un linguaggio dinamico, interpretato, il che significa che le informazioni sul tipo sono facilmente disponibili in fase di runtime.

Così il vostro esempio di cui sopra potrebbe essere semplice come

class C1(object): 
    pass 

class C2(object): 
    pass 

l = [C1(), C2()] 
if __name__=="__main__": 
    for element in l: 
     print type(element) 

che produrrà:

<class '__main__.C1'> 
<class '__main__.C2'> 
+0

Grazie per il vostro aiuto! Avrei accettato la tua risposta, ma quella di Tony era un po 'più dettagliata. – bogdan

8

È possibile utilizzare i decoratori per ottenere ciò che si desidera. Copia di un esempio da questo blog:

class Lion: pass 
class Tiger: pass 
class Bear: pass 

class ZooVisitor: 
    @visitor(Lion) 
    def visit(self, animal): 
     return "Lions" 

    @visitor(Tiger) 
    def visit(self, animal): 
     return "tigers" 

    @visitor(Bear) 
    def visit(self, animal): 
     return "and bears, oh my!" 

animals = [Lion(), Tiger(), Bear()] 
visitor = ZooVisitor() 
print(', '.join(visitor.visit(animal) for animal in animals)) 
# Prints "Lions, tigers, and bears, oh my!" 

e il codice per il @visitor decoratore (nel caso in cui il link si esaurisce):

# A couple helper functions first 

def _qualname(obj): 
    """Get the fully-qualified name of an object (including module).""" 
    return obj.__module__ + '.' + obj.__qualname__ 

def _declaring_class(obj): 
    """Get the name of the class that declared an object.""" 
    name = _qualname(obj) 
    return name[:name.rfind('.')] 

# Stores the actual visitor methods 
_methods = {} 

# Delegating visitor implementation 
def _visitor_impl(self, arg): 
    """Actual visitor method implementation.""" 
    method = _methods[(_qualname(type(self)), type(arg))] 
    return method(self, arg) 

# The actual @visitor decorator 
def visitor(arg_type): 
    """Decorator that creates a visitor method.""" 

    def decorator(fn): 
     declaring_class = _declaring_class(fn) 
     _methods[(declaring_class, arg_type)] = fn 

     # Replace all decorated methods with _visitor_impl 
     return _visitor_impl 

    return decorator 

blog correlati (primo sembra già essere giù): https://chris-lamb.co.uk/posts/visitor-pattern-in-python

EDIT:

obj.__qualname__ non disponibile fino a Python 3.3, quindi dobbiamo usare un hack per le versioni inferiori: -

def _qualname(obj): 
    """Get the fully-qualified name of an object (including module).""" 

    if hasattr(obj, '__qualname__'): 
     qualname = obj.__qualname__ 
    else: 
     qualname = str(obj).split(' ')[1] 

    return obj.__module__ + '.' + qualname 

Purtroppo la soluzione di cui sopra non funziona per le versioni di Python sotto 3.3, come i metodi sono funzioni ancora regolari quando passato a una decoratore. Puoi provare ad usare sia un decoratore di classi che di metodi, vedi Can a Python decorator of an instance method access the class?.