Skip to content Skip to sidebar Skip to footer

Pythonic Way To Cycle Through Purely Side-effect-based Comprehension

What is the most pythonic way to execute a full generator comprehension where you don't care about the return values and instead the operations are purely side-effect-based? An exa

Solution 1:

You do so by not using a generator expression.

Just write a proper loop:

for v in split_me:
    if v:
        a.append(v)
    else:
        b.append(v)

or perhaps:

for v in split_me:
    target = a if v else b
    target.append(v)

Using a generator expression here is pointless if you are going to execute the generator immediately anyway. Why produce an object plus a sequence of None return values when all you wanted was to append values to two other lists?

Using an explicit loop is both more comprehensible for future maintainers of the code (including you) and more efficient.

Solution 2:

itertools has this consume recipe

defconsume(iterator, n):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."# Use functions that consume iterators at C speed.if n isNone:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position nnext(islice(iterator, n, n), None)

in your case n is None, so:

collections.deque(iterator, maxlen=0)

Which is interesting, but also a lot of machinery for a simple task

Most people would just use a for loop

Solution 3:

As others have said, don't use comprehensions just for side-effects.

Here's a nice way to do what you're actually trying to do using the partition() recipe from itertools:

try:  # Python 3from itertools import filterfalse
except ImportError:  # Python 2from itertools import ifilterfalse as filterfalse
    from itertools import ifilter asfilterfrom itertools import tee


defpartition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'# From itertools recipes:# https://docs.python.org/3/library/itertools.html#itertools-recipes# partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

split_me = [0, 1, 2, None, 3, '']

trueish, falseish = partition(lambda x: x, split_me)

# You can iterate directly over trueish and falseish,# or you can put them into lists

trueish_list = list(trueish)
falseish_list = list(falseish)

print(trueish_list)
print(falseish_list)

Output:

[0, None, ''][1, 2, 3]

Solution 4:

There's nothing non-pythonic in writing things on many lines and make use of if-statements:

for v in split_me:
    if v:
        a.append(v)
    else:
        b.append(v)

If you want a one-liner you could do so by putting the loop on one line anyway:

for v in split_me: a.append(v) if v else b.append(v)

If you want it in an expression (which still beats me why you want unless you have a value you want to get out of it) you could use list comprehension to force looping:

[x for x in (a.append(v) if v else b.append(v) for v in split_me) if False]

Which solution do you think best shows what you're doing? I'd say the first solution. To be pythonic you should probably consider the zen of python, especially:

  • Readability counts.
  • If the implementation is hard to explain, it's a bad idea.

Post a Comment for "Pythonic Way To Cycle Through Purely Side-effect-based Comprehension"