Cohort 4Register

PART 3 · ADVANCED PYTHON · PRACTICE

Practice set 2: advanced

FreeLesson 38 of 78

Generators, itertools, decorators and classes, the way coding rounds ask for them.

Q12Write chunked(iterable, n) that yields lists of n items, without itertools.batched. It must work on a generator.

DE MEDIUM

Approach: islice pulls n items from one shared iterator until a slice comes back empty.

from itertools import islice

def chunked(iterable, n):
    it = iter(iterable)                  # one shared iterator
    while batch := list(islice(it, n)):
        yield batch

print(list(chunked(range(101, 108), 3)))
print(list(chunked((x for x in "abcde"), 2)))

Output

[[101, 102, 103], [104, 105, 106], [107]]
[['a', 'b'], ['c', 'd'], ['e']]

Q13Run-length encode a status stream: ["D","D","C","D","D","D"] becomes "D2C1D3".DEEASY

Approach: groupby on unsorted input is exactly right here: we want runs of neighbours.

from itertools import groupby

stream = ["D", "D", "C", "D", "D", "D"]
runs = (f"{k}{sum(1 for _ in g)}" for k, g in groupby(stream))
print("".join(runs))

Output

D2C1D3

Q14Write a decorator count_calls that records how many times each decorated function ran.DEMEDIUM

Approach: Store state on the wrapper itself, and keep the name with functools.wraps.

import functools

def count_calls(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        wrapper.calls += 1
        return fn(*args, **kwargs)
    wrapper.calls = 0
    return wrapper

@count_calls
def load(batch): return len(batch)

for b in ([1, 2], [3], [4, 5, 6]):
    load(b)
print(load.__name__, load.calls)

Output

load 3

Q15Sort employees.csv by department A to Z, then salary high to low, then name. Print the top two per

department. DE MEDIUM

Approach: One sort with a tuple key (salary negated), then groupby on the now-sorted department.

import csv
from itertools import groupby, islice

rows = list(csv.DictReader(open("employees.csv")))
rows.sort(key=lambda r: (r["dept"], -int(r["salary"]), r["name"]))
for dept, group in groupby(rows, key=lambda r: r["dept"]):
    top = [(r["name"], r["salary"]) for r in islice(group, 2)]
    print(dept, top)

Output

Analytics [('Ishita', '160000'), ('Neha', '150000')]
Engineering [('Anita', '250000'), ('Rahul', '180000')]
Sales [('Dev', '90000'), ('Tara', '90000')]

Practice before you move on

  1. Solve Q12 to Q15 in the Try it editor before you read each solution.
  2. Use chunked() from Q12 on a generator of rows from orders.csv and print each chunk's order_ids.