Есть ли в Python способ вызывать метод для каждого элемента итерируемого?
Возможный дубликат:
Есть ли карта без результата в Python?
Я часто сталкиваюсь с ситуацией в моих программах, когда я хочу быстро / эффективно вызвать метод на месте для каждого из элементов, содержащихся в итерируемом объекте. (Быстро означать, что издержки цикла for недопустимы). Хорошим примером будет список спрайтов, когда я хочу позвонить draw()
на каждом из Sprite
объекты.
Я знаю, что могу сделать что-то вроде этого:
[sprite.draw() for sprite in sprite_list]
Но я чувствую, что понимание списка неправильно используется, так как я не использую возвращенный список. То же самое касается map
функция. Забей меня на преждевременную оптимизацию, но я также не хочу накладных расходов на возвращаемое значение.
Я хочу знать, есть ли в Python метод, который позволяет мне делать то, что я только что объяснил, возможно, как гипотетическая функция, которую я предлагаю ниже:
do_all(sprite_list, draw)
2 ответа
Вы всегда можете написать свой собственный do_all
функция:
def do_all(iterable, func):
for i in iter(iterable):
func(i)
Тогда звоните, когда хотите.
Там действительно нет проблем с производительностью с использованием явного for
петля.
Существует проблема с производительностью использования списка или map
, но только в построении списка результатов. Очевидно, что итерация более 500 миллионов элементов будет намного медленнее, если вам придется составить список 500 миллионов по пути.
Здесь стоит указать, что это почти наверняка не произойдет для таких вещей, как составление списка спрайтов. У вас нет 500M спрайтов для рисования. И если вы это сделаете, это, вероятно, займет гораздо больше времени, чем создание списка 500 миллионов копий None. И в наиболее вероятных случаях, когда вам нужно сделать ту же самую простую вещь с 500M-объектами, есть лучшие решения, такие как переключение на numpy
, Но есть несколько возможных случаев, когда это может произойти.
Самый простой способ - использовать выражение-генератор или itertools.imap
(или, в Python 3, просто map
), а затем избавиться от значений, написав dispose
функция. Одна возможность:
def dispose(iterator):
for i in iterator:
pass
Затем:
dispose(itertools.imap(Sprite.draw, sprite_list))
Вы могли бы даже определить do_all
как:
def do_all(iterable, func):
dispose(itertools.imap(func, iterable))
Если вы делаете это для ясности или простоты, я думаю, что это неправильно. Версия цикла for легко читается, и эта версия выглядит так, как будто вы пытаетесь написать Haskell с неправильными именами и синтаксисом функций.
Если вы делаете это для повышения производительности... ну, если бы когда-либо существовала реальная ситуация с производительностью, когда это имело значение (что кажется маловероятным), вы, вероятно, захотите поиграть с кучей различных потенциальных реализаций dispose
и, возможно, переместить dispose
обратно в do_all
чтобы избежать дополнительного вызова функции, и, возможно, даже реализовать все это в C (заимствование кода быстрой итерации из файла sterlib's itertools.c).
Или лучше, pip install more-itertools
затем используйте more_itertools.consume
, Для чего это стоит, текущая версия просто делает collections.deque(iterator, maxlen=0)
и в тесте с пользовательской реализацией C она менее чем на 1% медленнее (за исключением очень маленьких итераторов - отсечка на моей системе равна 19), поэтому, вероятно, не стоит реализовывать на C. Но если кто-то это делает, или если некоторые будущий Python (или PyPy) обеспечивает более быстрый способ его реализации, скорее всего, он будет добавлен в more-itertools
прежде чем вы узнаете об этом и измените свой код.
Предполагая, что sprite_list является списком объектов Sprite, вы можете сделать:
map(Sprite.draw, sprite_list)
Это вызовет Sprite.draw() для каждого элемента в sprite_list
что по сути то же самое, что и понимание списка, которое вы опубликовали. Если вы не хотите создавать список, вы можете просто использовать цикл for:
for sprite in sprite_list:
sprite.draw()