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
The most obvious one is this:
I mean, there are some more exotic ways to do it.
Any iterable object can be expanded with the asterisk operator, we all do it every day:
But that extra method is unnecessary, because it is possible to just use asterisk inside square brackets:
Well, no. We can expand our iterable directly into a sequence, which is usually done like this:
The most pythonic way is the first one, when the
I hope there are no more simple (one-line) ways to solve the problem. But if you know one, please, do me a favor and write a comment.
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.
Any iterable object can be expanded with the asterisk operator, we all do it every day:
self.increase(self, *increments):
self.set_values(*map(sum, zip(self.values, increments)))
def set_values(self, *values):
self.values = values
This operator expands the iterable into a list of positional arguments, then set_values
method uses another asterisk to collect all positional arguments back into a list.But that extra method is unnecessary, because it is possible to just use asterisk inside square brackets:
def increase(self, *increments):
self.values = [*map(sum, zip(self.values, increments))]
Here, a list is created by the "square brackets statement" which gets its values from the expanded iterable. It is also possible to create a set using curly braces:def increase(self, *increments):
self.values = {*map(sum, zip(self.values, increments))}
But if you need a tuple you need to do something more then just replacing braces with parens:def increase(self, *increments):
self.values = (*map(sum, zip(self.values, increments)),)
That comma after the map
function call converts "just parens" into a tuple. Actually, there is no need in parens at all:def increase(self, *increments):
self.values = *map(sum, zip(self.values, increments)),
Is this exotic enough?Well, no. We can expand our iterable directly into a sequence, which is usually done like this:
a, b, c = range(3)
(a, b, c) = range(3)
[a, b, c] = range(3)
This it the simplest form of the feature, and here is more powerful one:def increase(self, *increments):
[*self.values] = map(sum, zip(self.values, increments))
Just like with arbitrary positional parameters, one sequence item, self.values
, captures many items from the source iterable into a single list. So, the most exotic way to expand an iterator into a tuple is this:def increase(self, *increments):
*self.values, = map(sum, zip(self.values, increments))
You don't need parens, remember? self.values
is still a list.The most pythonic way is the first one, when the
map
function call is directly wrapped by the tuple
. Some may prefer list comprehension, though.I hope there are no more simple (one-line) ways to solve the problem. But if you know one, please, do me a favor and write a comment.
Комментариев нет:
Отправить комментарий