Rearrange page numbers - follow-up This is a follow-up of: Rearrange page numbers for printing a book

Input:


Total pages of a book: int
Pages per booklet: int (divisible by 4 evenly)


Processing:

Rearrange the pages in each booklet to follow a specific format, [n, f, f+1, n-1], where n is the last page of the booklet, and f is the first.

Output:

Print a list/tuple of the rearranged integers

Bugs:

If pages is not a multiple of 4 the last booklet (list that get processed by arrange_booklet()) always has errors at the end. 

Misc:

int pages will generally be a large number (50~500). I wrote the program because my printer software doesn't allow me to just print the pages in this order. I have to manually type every integer, comma-separated. Which would obviously get tedious very quickly.

from math import ceil


# f is the first page (smallest input integer), n is the last (largest)
def arrange_booklet(f, n):
    '''Creates a list by using the page # order formula.'''
    _ = list()
    while n &gt; f:
        _.extend([n, f, f+1, n-1])
        f += 2
        n -= 2
    return _


def make_book():
    '''Combines all the lists into one and returns the result.'''
    pages = list(range(1, int(input("Enter a number of pages: "))+1))
    while True:
        len_booklet = int(input(
            "Enter booklet length, must be a multiple of four: "))
        if len_booklet % 4 == 0:
            break
    book = list()  # permanent list, to be returned
    _ = list()  # temporary list, to populate permanent list
    for page in pages:
        _.append(page)
        if page % len_booklet == 0:
            _ = arrange_booklet(_[0], _[-1])
            book.extend(_)
            _.clear()
    # the last booklet, if pages % 4 != 0, is still in the temp list and broke
    # out of the loop it has to be handled separately below, or it will never
    # reach the book
    if _:
        _ = arrange_booklet(_[0], _[-1])
        book.extend(_)
    print(book)


if __name__ == '__main__':
    make_book()
Functions

you should split a lot more of the small pieces of code to separate functions, For example getting and parsing the input has no place in the make_book

Generators

Instead of using temporary lists, using generators is a lot cleaner.

so instead of 

def my_func():
    _ = []
    for i in range(10):
        _.append(i**2)
    return _


with a generator this becomes:

def my_gen():
    for i in range(10):
        yield i**2


This is a lot cleaner, and easier to maintain.

Assembling the booklet

instead of iterating over the indices f and n, you can use a collections.dequeand use pop and popleft

from collections import deque
def assemble_booklet(pages):
    """assembles the booklet according to the order [-1, 0, 1, -2] and going in.

    Assumes an iterable with the length a multiple of 4"""
    pages = deque(pages)
    assert not len(pages) % 4, 'booklet length, must be a multiple of 4'
    while pages:
        selection = pages.pop(), pages.popleft(), pages.popleft(), pages.pop()
        yield from selection


again, the generator style allows us to express this a lot clearer than using the intermediary lists

grouping in pages

grouping the pages can be easily done with the grouper itertools recipe

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    """Collect data into fixed-length chunks or blocks

    grouper('ABCDEFG', 3, 'x') --&gt; ABC DEF Gxx"
    https://docs.python.org/3/library/itertools.html#itertools-recipes
    """
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)


Then:

for page in pages:
    _.append(page)
    if page % len_booklet == 0:
        _ = arrange_booklet(_[0], _[-1])
        book.extend(_)
        _.clear()


becomes:

def make_book(pages, booklet_length):
    '''Combines all the lists into one and returns the result.'''
    assert not booklet_length % 4, 'booklet length, must be a multiple of 4'
    for group in grouper(pages, booklet_length, fillvalue='blank'):
        yield tuple(assemble_booklet(group))
        # yield from assemble_booklet(group) # dependent on the output you want


If you want the pages in 1 long list instead of separated per booklet, you can change the last line of make_book to yield from assemble_booklet(group)

And in effect, this is the whole code for this method, that gets an iterable of pages, and form booklets out of them

if __name__ == '__main__':
    pages = range(1, 16)
    booklet_length = 8
    result = list(make_book(pages, booklet_length))
    print(result)



[(8, 1, 2, 7, 6, 3, 4, 5), ('blank', 9, 10, 15, 14, 11, 12, 13)]