2010-04-21 5 views
42

Пусть следующий код:Как вызвать тот же метод для списка объектов?

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

Теперь я хочу вызвать методы запуска и остановки (возможно, также другие) для каждого объекта в списке всех. Есть ли элегантный способ для этого, кроме того, чтобы писать кучу функций, как

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

def stop_all(all): 
+1

связка функции есть две функции? – SilentGhost

+3

Это не связано с вашим вопросом, но в вашем примере нет очевидной причины, почему вам нужен этот базовый класс. Python будет очень рад предоставить вам список несвязанных объектов, и пока все они имеют методы «начать» и «остановить», вы все равно можете перебирать их, вызывая методы. – Duncan

+1

Ваша определяющая база с бесполезными методами, а затем определение поведения в 'A' и' B' отражает плохой дизайн в Python. Вместо использования абстрактного базового класса вы можете просто определить 'A' и' B' и использовать их взаимозаменяемо, поскольку они имеют общий интерфейс. Ваш текущий способ делать вещи создает бесполезный класс, который просто лишний материал вам не нужен. –

ответ

3

Функции * _all() настолько просты, что для нескольких методов я просто написал функции. Если у вас есть много одинаковых функций, вы можете написать общую функцию:

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

Или создать функциональный завод:

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

Это не кажется мне проще, чем писать цикл for напрямую. –

6

возможно map, но так как вы не хотите, чтобы сделать список, вы можете написать свой собственный ...

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

, то вы можете сделать:

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

, кстати, все встроенные в функции, не перезаписывать его ;-)

14

Подход

for item in all: 
    item.start() 

является простым, легким, читаемый , и кратким. Это основной подход, который Python обеспечивает для этой операции. Вы можете, конечно, инкапсулировать его в функцию, если это что-то помогает. Определение специальной функции для этого для общего использования, вероятно, будет менее ясным, чем просто выписывание цикла for.

2

Принимая @Ants Aasmas ответить на один шаг дальше, вы можете создать оболочку, которая принимает любой вызов метода и направляет его ко всем элементам данного списка:

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 

Этот класс может быть использован, как это:

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

Который производит следующий вывод:

foo: foo 
foo: bar 
foo: quux!

с некоторой работой и изобретательность, возможно, она была бы усилена для обработки атрибутов (возврат списка значений атрибутов).

18

Похоже, что это будет более Pythonic способ сделать это, но я еще не нашел его.

Я использую «карту» иногда, если я звоню ту же функцию (не метод) на кучу объектов:

map(do_something, a_list_of_objects) 

Это заменяет кучу кода, который выглядит следующим образом:

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

Но также может быть достигнуто с пешеходом «для» цикла:

for obj in a_list_of_objects: 
     do_something(obj) 

недостатком является то, что а) вы создавая список как возвращаемое значение из «карты», которое просто выбрасывается, и б) это может быть более запутанным, чем просто простой вариант цикла.

Вы также можете использовать список понимание, но это немного оскорбительным, а также (еще раз, создавая одноразовый список):

[ do_something(x) for x in a_list_of_objects ] 

Для методов, я полагаю, любые из них будет работать (с та же оговорка):

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

или

[ x.method_call() for x in a_list_of_objects ] 

Таким образом, на самом деле, я думаю, что р (но эффективный) «для» цикла, вероятно, лучший выбор.

+3

Моей первой реакцией была карта (лямбда x: x.member(), список). Он чувствовал себя довольно четким лайнером. –

100

Это будет работать

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

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

простой пример

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

и Python3

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

В карте Python 3 возвращается объект карты, а не список, как в Python 2. Я не уверен в стандартной процедуре, но я думал, что должен ее исправить, поэтому я представил редактирование. Таким образом, код должен работать как ожидалось, так и корректно как в Python 2, так и в Python 3. – leetNightshade

+1

Также это похоже на более ответ Pythonic, он должен быть принят. карта - хороший выбор, и до сих пор я никогда не использовал лямбда, поэтому спасибо. – leetNightshade

+0

не использовали python 3 много, но более общее решение не должно повредить (и я не думаю, что это делает пример менее понятным). На самом деле у меня не было возможности работать с питоном столько, сколько хотелось бы за последний год. –

Смежные вопросы