google web font

понедельник, 14 марта 2016 г.

How to expand an iterator into a list in Python3

One of the most noticeable changes from Python 2 to Python 3 is that map, filter, zip and range builtins now return iterators instead of lists. This is usually a good feature to have, because, well, nobody uses range outside of a for statement. But there may be rare cases when resulting list might take less memory then the iterator itself, especially when you nesting them like this:
def increase(self, *increments):
    self.values = map(sum, zip(self.values, increments))
This is going to eat all the memory and crash with a several gigabyte core dump. So the question is: how does one expand an iterator into a sequence in Python 3? Well, I don't want to hurt the Zen of Python, but there are more than one way to do so.

The most obvious one is this:
def increase(self, *increments):
    self.values = tuple(map(sum, zip(self.values, increments)))
It's too obvious to write the whole post about it. It is as obvious as to get rid of the iterator in favor of the list comprehension:
def increase(self, *increments):
    self.values = [sum(x) for x in zip(self.values, increments)]
(Don't forget to use square brackets, or else you will get a generator, which is practically the same as iterator in this case).

I mean, there are some more exotic ways to do it.

Сколькими способами можно развернуть генератор в список в Python3

Одно из заметных нововведений в Python3 по сравнению с Python2 это то, что встроенные функции filter, map, range и zip теперь возвращают итераторы. В некоторых случаях этот итератор может занимать больше памяти, чем тот список, который от него хотят. А если их ещё и засовывать один в другой, вроде чего-то такого:
def increase(self, *increments):
    self.values = map(sum, zip(self.values, increments))
так вообще никакой памяти не хватит. Отсюда, конечно вопрос: как развернуть этот итератор в нормальную последовательность? Как бы сильно это ни противоречило Дзену Пайтона, способов много.
Самый очевидный вариант:
def increase(self, *increments):
    self.values = tuple(map(sum, zip(self.values, increments)))
Слишком просто, чтобы писать об этом блог-пост. С таким же успехом можно было вообще избавиться от map, использовав list-comprehension:
def increase(self, *increments):
    self.values = [sum(x) for x in zip(self.values, increments)]
(обязательно квадратные скобки, а то генератор никуда не денется :-)
В общем, есть более экзотические варианты.