Pull-up sequence accumulator counter I wanted to create a function describing a sport game called "Leader". The idea is that you make as many push-ups as you can, increasing each repetition by 1 and as you reach your maximum, each next repetition is decreased by 1 until you reach 0 push-ups eventually.

I managed to do this using dictionaries, but I think this could be done in much easier way.

from typing import List, Tuple


def leader_step(max_pushups, step): # maximum pushups a person can do and a step of increment
    i = 0  # count of the repetitions
    pushups: List[Tuple[int, int]] = [(0, 0)]  # number of pushups at the beginning (at each repetition, it total)
    while pushups[i][0] &lt;= max_pushups + abs(step): # +abs(step) in case step &gt; 1
        if pushups[i][0] &gt;= max_pushups:  # decrease push-ups as they reach max
            step = -step
        i += 1
        now = step + pushups[i - 1][0]
        sum = now + pushups[i - 1][1]  # counting the sum of all push-ups by adding previous sum and current pushups
        pushups.insert(i, (now, sum))
        if pushups[i][0] &lt; 1:  # game stops when you reach 0 push-up
            break
    return pushups[1:-1]


Function should return 2 sequences: 


showing the number of push-ups at each repetition 
showing total sum of push-ups made at each repetition
You can indeed simplify this quite a bit using a generator and the itertools module.

I would separate out the generating of the pushups to be done from the total pushups. For this you can use two range objects and the yield from (Python 3.3+) keyword combination:

def pushups(n):
    yield from range(1, n)
    yield from range(n, 0, -1)


The accumulation can be done using itertools.accumulate and itertools.tee to duplicate the generator:

from itertools import accumulate, tee

def leader_step(n):
    gen1, gen2 = tee(pushups(n))
    return list(gen1), list(accumulate(gen2))

if __name__ == "__main__":
    print(leader_step(5))
# ([1, 2, 3, 4, 5, 4, 3, 2, 1], [1, 3, 6, 10, 15, 19, 22, 24, 25])


As noted in the comments by @Peilonrayz, it is not actually necessary to split the generator (as long as it fits into memory, which is very likely, given that presumably a human will try to do this training):

def leader_step(n):
    training = list(pushups(n))
    return training, list(accumulate(training))