What is a nice, python style way to yield ranged subsets of a string/bytes/list several items at a time?

Question:

I want to loop over bytes data, and I’d hope the same principle would apply to strings and lists, where I don’t go item by item, but a few items at a time. I know I can do mystr[0:5] to get the first five characters, and I’d like to do that in a loop.

I can do it the C style way, looping over ranges and then returning the remaining elements, if any:

import math
def chunkify(listorstr, chunksize:int):
    # Loop until the last chunk that is still chunksize long
    end_index = int(math.floor(len(listorstr)/chunksize))
    for i in range(0, end_index):
        print(f"yield ")
        yield listorstr[i*chunksize:(i+1)*chunksize]
    # If anything remains at the end, yield the rest
    remainder = len(listorstr)%chunksize
    if remainder != 0:
        yield listorstr[end_index*chunksize:len(listorstr)]
        
[i for i in chunkify("123456789", 2)]

This works just fine, but I strongly suspect python language features could make this a lot more compact.

Asked By: Tomáลก Zato

||

Answers:

You can condense your code using the range step parameter. Instead of the for loop, a generator for this is

listorstr[i:i+chunksize] for i in range(0,len(listorstr), chunksize)

Your function could yield from this generator to make for a more tidy call.

def chunkify(listorstr, chunksize:int):
    yield from (listorstr[i:i+chunksize] 
        for i in range(0,len(listorstr), chunksize))
Answered By: tdelaney