2010-04-21 20 views
42

código Supongamos la siguiente manera:¿Cómo llamar al mismo método para obtener una lista de objetos?

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,.....] 

Ahora quiero llamar comienzan métodos y detener (tal vez también otros) para cada objeto en la lista de todos. ¿Hay alguna manera elegante para hacer esto, excepto de escribir un montón de funciones como

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

def stop_all(all): 
+1

un montón de función es dos funciones? – SilentGhost

+3

Esto no está relacionado con su pregunta, pero no hay una razón obvia en su ejemplo por la que necesita esa clase base. Python estará encantado de permitirle tener una lista de objetos no relacionados y, siempre que todos tengan métodos de "inicio" y "detención", puede iterar a través de ellos llamando a los métodos. – Duncan

+1

Su 'Base' definitoria con métodos inútiles y luego definir el comportamiento en 'A' y 'B' refleja un diseño deficiente en Python. En lugar de utilizar una clase base abstracta, puedes simplemente definir 'A' y 'B' y usarlos indistintamente en la medida en que compartan una interfaz. Tu manera actual de hacer las cosas crea una clase inútil, que es solo algo extra que no necesitas. –

Respuesta

3

Las funciones de * _all() son tan simples que para algunos métodos solo escribo las funciones. Si usted tiene un montón de funciones idénticas, puede escribir una función genérica:

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

O crear una fábrica de función:

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

Esto no me parece tan simple como escribir el ciclo for directamente. –

6

quizá map, pero ya que no quiere hacer una lista, usted puede escribir su propia ...

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

entonces usted puede hacer:

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

por cierto, todo está construido en función, no sobrescribir ello ;-)

14

El enfoque

for item in all: 
    item.start() 

es simple, fácil y legible y conciso. Este es el enfoque principal que Python proporciona para esta operación. Ciertamente puede encapsularlo en una función si eso ayuda a algo. Es probable que definir una función especial para este uso general sea menos claro que escribir el bucle for.

2

Tomando @Ants Aasmas responder a un paso más allá, se puede crear un contenedor que toma cualquier llamada al método y lo reenvía a todos los elementos de una lista dada:

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 

Esa clase, entonces se puede utilizar la siguiente manera:

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

que produce el siguiente resultado:

foo: foo 
foo: bar 
foo: quux!

Con algo de trabajo y el ingenio probablemente podría mejorarse para manejar los atributos también (devolviendo una lista de valores de atributo).

18

Parece que habría una forma más pitonica de hacerlo, pero todavía no lo he encontrado.

utilizo "mapa" a veces si estoy llamando a la misma función (no es un método) en un montón de objetos:

map(do_something, a_list_of_objects) 

Esto sustituye a un montón de código que tiene este aspecto:

también se puede lograr con un peatón bucle "for"
do_something(a) 
do_something(b) 
do_something(c) 
... 

Pero:

for obj in a_list_of_objects: 
     do_something(obj) 

La desventaja es que a) eres crear una lista como valor de retorno de "mapa" que simplemente está siendo descartada yb) podría ser más confuso que solo la variante de bucle simple.

También es posible usar una lista por comprensión, pero eso es un poco abusivo, así (una vez más, la creación de una lista de usar y tirar):

[ do_something(x) for x in a_list_of_objects ] 

Para los métodos, supongo que cualquiera de ellos podría funcionar (con las mismas reservas):

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

o

[ x.method_call() for x in a_list_of_objects ] 

Así que, en realidad, creo que el p El bucle edetrian (pero efectivo) "for" es probablemente la mejor opción.

+3

Mi primera reacción fue el mapa (lambda x: x.member(), list). Se sentía como un trazador de líneas bastante claro –

100

Esto funcionará

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

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

ejemplo sencillo

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

y en python3

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

En el mapa Python 3 devuelve un objeto de mapa, no una lista como en Python 2. No estoy seguro de procedimiento estándar, pero pensé que debería corregirlo, por lo que envié una edición. Entonces, el código ahora debería funcionar como se esperaba y correctamente tanto en Python 2 como en Python 3. – leetNightshade

+1

También se siente como la respuesta más pitonica, debería ser la aceptada. el mapa es una buena opción, y nunca antes había usado lambdas, así que gracias. – leetNightshade

+0

no han usado mucho Python 3, pero una solución más general no debería doler (y no creo que haga el ejemplo menos claro). En realidad, no he tenido la oportunidad de trabajar con Python tanto como quisiera el año pasado. –

Cuestiones relacionadas