2010-04-21 13 views
42

codice Supponiamo che in questo modo:Come chiamare lo stesso metodo per un elenco di oggetti?

class Base: 
    def start(self): 
     pass 
    def stop(self) 
     pass 

class A(Base): 
    def start(self): 
     ... do something for A 
    def stop(self) 
     .... do something for A 

class B(Base): 
    def start(self): 

    def stop(self): 

a1 = A(); a2 = A() 
b1 = B(); b2 = B() 

all = [a1, b1, b2, a2,.....] 

Ora voglio chiamare i metodi avviare e fermare (forse anche altri) per ogni oggetto in tutta la lista. C'è un modo elegante per fare questo, tranne di scrivere una serie di funzioni come

def start_all(all): 
    for item in all: 
     item.start() 

def stop_all(all): 
+1

un mazzo di funzione è funzione due? – SilentGhost

+3

Questo non è correlato alla tua domanda, ma nel tuo esempio non c'è una ragione ovvia per cui hai bisogno di quella classe base. Python sarà molto felice di farti avere una lista di oggetti non correlati e fintanto che tutti hanno metodi 'start' e 'stop' puoi ancora iterare attraverso loro chiamando i metodi. – Duncan

+1

La definizione di 'Base' con metodi inutili e quindi la definizione del comportamento in' A' e 'B' riflette un design scadente in Python. Piuttosto che usare una classe base astratta, puoi semplicemente definire 'A' e' B' e usarli in modo intercambiabile nella misura in cui condividono un'interfaccia. Il tuo attuale modo di fare le cose crea una classe inutile, che è solo materiale extra di cui non hai bisogno. –

risposta

3

Le funzioni * _all() sono così semplici che per alcuni metodi scriverei semplicemente le funzioni. Se hai un sacco di funzioni identiche, è possibile scrivere una funzione generica:

def apply_on_all(seq, method, *args, **kwargs): 
    for obj in seq: 
     getattr(obj, method)(*args, **kwargs) 

O creare una fabbrica funzione:

def create_all_applier(method, doc=None): 
    def on_all(seq, *args, **kwargs): 
     for obj in seq: 
      getattr(obj, method)(*args, **kwargs) 
    on_all.__doc__ = doc 
    return on_all 

start_all = create_all_applier('start', "Start all instances") 
stop_all = create_all_applier('stop', "Stop all instances") 
... 
+27

Questo non mi sembra più semplice della scrittura diretta del ciclo for. –

6

forse map, ma dal momento che non si vuole fare una lista, è possibile scrivere il proprio ...

def call_for_all(f, seq): 
    for i in seq: 
     f(i) 

allora si può fare:

call_for_all(lamda x: x.start(), all) 
call_for_all(lamda x: x.stop(), all) 

tra l'altro, tutto è costruito in funzione, non sovrascrivere ;-)

14

L'approccio

for item in all: 
    item.start() 

è semplice, facile, leggibile e conciso. Questo è l'approccio principale che Python fornisce per questa operazione. Puoi certamente incapsularlo in una funzione se questo aiuta qualcosa. È probabile che la definizione di una funzione speciale per questo per uso generale sia meno chiara della semplice scrittura del ciclo for.

2

Prendendo @Ants Aasmas rispondere a un ulteriore passo avanti, è possibile creare un wrapper che prende qualsiasi chiamata di metodo e l'inoltra a tutti gli elementi di una lista data:

class AllOf: 
    def __init__(self, elements): 
     self.elements = elements 
    def __getattr__(self, attr): 
     def on_all(*args, **kwargs): 
      for obj in self.elements: 
       getattr(obj, attr)(*args, **kwargs) 
     return on_all 

Quella classe può quindi essere utilizzato in questo modo:

class Foo: 
    def __init__(self, val="quux!"): 
     self.val = val 
    def foo(self): 
     print "foo: " + self.val 

a = [ Foo("foo"), Foo("bar"), Foo()] 
AllOf(a).foo() 

che produce il seguente output:

foo: foo 
foo: bar 
foo: quux!

Con un po 'di lavoro e l'ingenuità potrebbe probabilmente essere migliorata per gestire anche gli attributi (restituendo un elenco di valori degli attributi).

18

Sembra che ci sarebbe un modo più pitonico per farlo, ma non l'ho ancora trovato.

io uso "mappa" a volte se sto chiamando la stessa funzione (non un metodo) su un mucchio di oggetti:

map(do_something, a_list_of_objects) 

Questo sostituisce un mucchio di codice che assomiglia a questo:

do_something(a) 
do_something(b) 
do_something(c) 
... 

ma può anche essere realizzato con un pedone ciclo "for":

for obj in a_list_of_objects: 
     do_something(obj) 

Il rovescio della medaglia è che a) sei creare una lista come valore di ritorno da "mappa" che è appena stata lanciata e b) potrebbe essere più confusa che solo la semplice variante di loop.

Si potrebbe anche usare una lista di comprensione, ma questo è un po 'abusiva e (ancora una volta, la creazione di un elenco e getta):

[ do_something(x) for x in a_list_of_objects ] 

Per i metodi, suppongo che uno di questi avrebbe funzionato (con le stesse prenotazioni):

map(lambda x: x.method_call(), a_list_of_objects) 

o

[ x.method_call() for x in a_list_of_objects ] 

Quindi, in realtà, penso che il p edestre (ma efficace) "for" loop è probabilmente la soluzione migliore.

+3

La mia prima reazione è stata la mappa (lambda x: x.member(), elenco). Sembrava una fodera piuttosto chiara. –

100

Ciò funzionerà

all = [a1, b1, b2, a2,.....] 

map(lambda x: x.start(),all)  

semplice esempio

all = ["MILK","BREAD","EGGS"] 
map(lambda x:x.lower(),all) 
>>>['milk','bread','eggs'] 

in python3

all = ["MILK","BREAD","EGGS"] 
list(map(lambda x:x.lower(),all)) 
>>>['milk','bread','eggs'] 
+2

Nella mappa Python 3 restituisce un oggetto mappa, non una lista come in Python 2. Non sono sicuro della procedura standard, ma ho pensato di doverlo correggere, quindi ho inviato una modifica. Quindi il codice dovrebbe funzionare come previsto e correttamente sia in Python 2 che in Python 3. – leetNightshade

+1

Anche questo sembra la risposta più pitone, dovrebbe essere accettata. la mappa è una buona scelta, e non ho mai usato lambda prima d'ora, quindi grazie. – leetNightshade

+0

non ho usato molto Python 3, ma una soluzione più generale non dovrebbe ferire (e non penso che possa rendere l'esempio meno chiaro). In realtà non ho avuto la possibilità di lavorare con Python tanto quanto vorrei nell'ultimo anno. –

Problemi correlati