Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python  Module 19 – Python for coding interviews and online assessments

Python for coding rounds: input, output, limits

Read a million numbers fast with sys.stdin.buffer, write all answers in one call, avoid the recursion and digit limits, and start from one template.

  • Intermediate
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free: 25 minutes

What you will learn

  • Explain how a judge runs and checks a program, and keep standard output to the answers alone
  • Set up a template that reads the whole input at once, solves, and writes every answer in one call
  • Choose when to flush output, for ordinary and for interactive problems
  • Convert a deep recursive search into a loop with an explicit stack
  • Avoid the integer digit limit and features newer than the judge's Python

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain how a judge runs and checks a program, and keep standard output to the answers alone
  • Set up a template that reads the whole input at once, solves, and writes every answer in one call
  • Choose when to flush output, for ordinary and for interactive problems
  • Convert a deep recursive search into a loop with an explicit stack
  • Avoid the integer digit limit and features newer than the judge's Python

Before you start

On this page

A coding round runs your program on an online judge: A service that runs a submitted program on hidden tests: it gives each test's input on standard input, compares everything the program prints with the expected answer, and gives a verdict for every test, under a time limit and a memory limit.: each test’s input arrives on standard streams: The three text channels every program starts with: standard input to read from, standard output for its results and standard error for messages. In Python they are sys.stdin, sys.stdout and sys.stderr. More in “Input and output: print(), input() and stdin”, everything the program prints is compared with the expected answer, and the run must finish within a time limit, often one or two seconds, and a memory limit. Python is fast enough for these limits when a solution keeps four habits: read the whole input in one call with sys.stdin.buffer.read().split(), collect the answers and write them in one call, replace recursion with a loop when the depth grows with the input, and remember that an int never overflows but refuses to become more than 4,300 decimal digits of text.

Each habit is measured or shown below on CPython 3.14.8, with the time it saves or the error it prevents. You will step through one test of a judge and the template’s walk over its input, and leave with a template to start every solution from.

A judge compares everything you print

Each test runs your program the same way, from its input to a verdict:

A judge feeds a test input file to python3 solution.py on standard input and compares what it prints with the expected answer.Test inputa file only the judge seespython3 solution.pywithin the time limitand the memory limitEverything the program printedExpected answerVerdict for this testaccepted · wrong answertime limit · runtime errorstandard inputstandard outputcomparedcompared

What a judge does with your program on every test

Text description of the diagram

The diagram shows one test of an online judge, from top to bottom.

  • At the top is the test input, a file that only the judge sees.
  • An arrow labelled standard input leads to the box python3 solution.py, which runs within the time limit and the memory limit of the problem.
  • An arrow labelled standard output leads from the program to everything it printed.
  • Two arrows labelled compared lead from the printed output and from the expected answer to the verdict for this test: accepted, wrong answer, time limit exceeded or runtime error.

A solution is accepted only when every test gets the verdict accepted, so the slowest and deepest test decides.

Nobody types anything and nobody reads a prompt. Two consequences catch people out in their first round:

  • A prompt is part of your answer. input("Enter n: ") writes Enter n: to standard output, so the judge sees it in front of your first answer and reports a wrong answer. Call input() without a prompt, or better, read the input the way the next section does.
  • The slowest test decides. A solution is accepted only if every test passes, and the last tests are usually the largest the problem allows. Read the limits in the statement, such as “n ≤ 2 × 10⁵”, as the size your program must handle, not the size of the examples.

Each judge states how strictly it compares, and many ignore spaces at the ends of lines and the final line break. An extra word or a stray debugging line is a wrong answer everywhere, so debugging output belongs on standard error (print(..., file=sys.stderr)), which judges do not compare with the answer. Follow one test of a program that still asks for its input with a prompt, and watch where each line it prints ends up:

Interactive · message by message One test of a program that still prints a prompt Step through one test. Arrows are what travels between the judge, your program and standard error, in time order.
One test of a program that still prints a prompt, step 7 of 7. Messages so far: Judge to solution.py: test 1 input; solution.py to Judge: Enter n: ; solution.py to Judge: 2, 0 and 1; solution.py to Standard error: 300.Judgeall tests: accepted?solution.pyinput() with no promptStandard errorholds 300, not comparedtest 1 inputEnter n: 2, 0 and 1300

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The test starts. The judge starts python3 solution.py with the input of test 1 already waiting on its standard input. Nobody types, and nothing announces that a test has begun.
  2. A prompt is printed. The program calls input("Enter n: "). input() writes its prompt to standard output, which is exactly where the judge collects the answer.
  3. The answers follow. The program reads the rest of the input and prints its three answers, 2, 0 and 1, one per line, the right numbers for this test.
  4. A debugging line. print(total, file=sys.stderr) writes to standard error instead. The judge keeps that stream apart, so the line never reaches the comparison.
  5. The comparison. The judge compares the output with the expected answer line by line. Its first line is "Enter n: 2" where 2 is expected, so the verdict for test 1 is wrong answer, although every number is right.
  6. Without the prompt. Called as input(), with no prompt, it writes nothing. The output is then 2, 0 and 1 alone, it matches line for line, and test 1 is accepted.
  7. Every test must pass. The judge runs the same steps for every test, and the last ones are usually the largest the statement allows. One test over the time limit fails the whole submission.

Every number was right and the verdict was still wrong, because the judge compares the text on standard output and nothing else. Removing the prompt fixed the test without touching the logic.

Checkpoint

Check: How a judge runs and checks a program

  1. Question 1 of 2 A solution reads n with int(input("Enter n: ")) and then prints the right answer, 2. The expected answer is 2. Which verdict does the test get?

    Choose one answer.

    A hint

    Think about everything the program wrote to standard output, in order.

    Show the answer: Question 1 of 2

    Answer: Wrong answer, because the output's first line is "Enter n: 2"

    The prompt "Enter n: " is written to standard output, the stream the judge compares, so the first line reads "Enter n: 2" instead of "2". Call input() without a prompt, or read the whole input with sys.stdin.buffer.

  2. Question 2 of 2 Where should a solution print the values it shows while you debug it, so that the judge ignores them?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: To standard error, with print(..., file=sys.stderr)

    Standard error is a separate stream that judges keep apart from the answer. print(total, file=sys.stderr) shows the value while you work, and the comparison never sees it.

One template: read once, solve, write once

The template keeps reading and writing in one place and the thinking in another. The example problem: each class lists its marks, and the answer is how many marks are above the class average.

Read once, solve, write once Python · template.py
import sys


def solve(tokens):
    """Answers every test case: how many marks are above the class average."""
    it = iter(tokens)
    out = []
    for _ in range(int(next(it))):
        n = int(next(it))
        marks = [int(next(it)) for _ in range(n)]
        total = sum(marks)
        # m > total / n, compared without floats: m * n > total
        out.append(str(sum(1 for m in marks if m * n > total)))
    return out


def main():
    tokens = sys.stdin.buffer.read().split()
    sys.stdout.write("\n".join(solve(tokens)) + "\n")


if __name__ == "__main__":
    main()

Input (standard input)

3
5
40 75 90 60 35
4
50 50 50 50
3
10 20
31

Output

2
0
1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 template.py

  • main() reads every byte of standard input in one call and splits it on any whitespace, so it does not matter how the numbers are spread over lines. The third class has its last mark on a line of its own, and nothing breaks.
  • solve() walks through the tokens with an iterator: next(it) takes the next token wherever it is. It returns the answers as a list of strings instead of printing them, which makes it easy to test: a test imports solve and compares its result, as this lesson’s exercise does.
  • main() writes all the answers with one sys.stdout.write(). The if __name__ == "__main__": guard, the main guard: The block under if __name__ == "__main__": in a file. It runs when the file is started as the program and is skipped when the file is imported, because __name__ then holds the module's own name. More in “Modules, imports and __name__”, runs it only when the file is run as a program, not when a test imports it. Keeping the work inside functions also makes it faster, because Python reaches a function’s local variables more quickly than names at module level; the next lesson measures the difference.
  • The comparison m * n > total asks “is m above the average?” without dividing. Whole numbers stay exact at any size, while a float average such as 61 ÷ 3 is rounded to about 16 significant digits, which can flip a comparison once the numbers are huge.

Step through the same walk over the same input, written out at the top level with a loop for each mark, so that every token the iterator hands over is a step of its own. The flag under tokens is the token the next next(it) hands over:

Interactive · line by line One read, then next(it) walks the tokens The flag under tokens is the token that the next next(it) hands over. Watch it cross the line breaks of the input as if they were not there.

Program · token_walk.py

data = (b"3\n5\n40 75 90 60 35\n"
        b"4\n50 50 50 50\n"
        b"3\n10 20\n31\n")
tokens = data.split()
it = iter(tokens)
out = []
for _ in range(int(next(it))):
    n = int(next(it))
    marks = [0] * n
    for i in range(n):
        marks[i] = int(next(it))
    total = sum(marks)
    above = sum(1 for m in marks if m * n > total)
    out.append(str(above))
print("\n".join(out))

Variables

After line 15: data is b'3\n5\n40 75 90 60 35\n4\n50 50 50 50\n3\n10 20\n31\n'; tokens is [b'3', b'5', b'40', b'75', b'90', b'60', b'35', b'4', b'50', b'50', b'50', b'50', …] (the loop reads position 16 next); it is <list_iterator object>; out is ['2', '0', '1']; _ is 2; n is 3; marks is [10, 20, 31]; i is 2; total is 61; above is 1.datab'3\n5\n40 75 90 60 35\n4\n50 50 50 50\n3\n10 20\n31\n'tokensb'3'0b'5'1b'40'2b'75'3b'90'4b'60'5b'35'6b'4'7b'50'8b'50'9b'50'10b'50'11b'3'12b'10'13b'20'14b'31'1516itit<list_iterator object>out'2'0'0'1'1'2_2n3marks100201312i2total61above1
  • just taken by the loop
  • changed in this step
  • passed by the loop
  • read next
  • where the loop stopped
  • the stop value, never given

Printed so far

2
0
1

Every step was recorded from a run with Python 3.14.8 on macOS 26 arm64.

The drawing shows the variables when the program ends, and the program's 52 steps are listed below.

Line run 15
Next line end
Loop pass –
Every step as text
  1. Start. No line has run yet, so there are no variables. Python starts with line 1.
  2. Line 1. data = (b"3\n5\n40 75 90 60 35\n" b"4\n50 50 50 50\n" b"3\n10 20\n31\n") sets data to b'3\n5\n40 75 90 60 35\n4\n50 50 50 50\n3\n10 20\n31\n'.
  3. Line 4. tokens = data.split() sets tokens to [b'3', b'5', b'40', b'75', b'90', b'60', b'35', b'4', …]. split() with no argument splits on any run of spaces and line breaks, so the eight lines of input become one flat list of 16 tokens, each of them bytes.
  4. Line 5. it = iter(tokens) sets it to <list_iterator object>. Key point it is an iterator over tokens. Each next(it) hands over the token under the flag and moves the flag one place on, so no line of the program counts positions.
  5. Line 6. out = [] makes out a list of 0 items.
  6. Line 7. for _ in range(int(next(it))): gives its first number: _ is 0.
  7. Line 8. n = int(next(it)) sets n to 5. The 5 says how many marks the first class has. The marks are simply the next five tokens, wherever their lines end.
  8. Line 9. marks = [0] * n sets marks to [0, 0, 0, 0, 0].
  9. Line 10. for i in range(n): gives its first number: i is 0.
  10. Line 11. marks[i] = int(next(it)) changes marks: it is now [40, 0, 0, 0, 0].
  11. Line 10. for i in range(n): gives the next number: i is 1.
  12. Line 11. marks[i] = int(next(it)) changes marks: it is now [40, 75, 0, 0, 0].
  13. Line 10. for i in range(n): gives the next number: i is 2.
  14. Line 11. marks[i] = int(next(it)) changes marks: it is now [40, 75, 90, 0, 0].
  15. Line 10. for i in range(n): gives the next number: i is 3.
  16. Line 11. marks[i] = int(next(it)) changes marks: it is now [40, 75, 90, 60, 0].
  17. Line 10. for i in range(n): gives the next number: i is 4.
  18. Line 11. marks[i] = int(next(it)) changes marks: it is now [40, 75, 90, 60, 35].
  19. Line 10. for i in range(n): has no number left: a range stops before its stop value, 5, so the loop ends.
  20. Line 12. total = sum(marks) sets total to 300.
  21. Line 13. above = sum(1 for m in marks if m * n > total) sets above to 2.
  22. Line 14. out.append(str(above)) adds '2' to the end of out.
  23. Line 7. for _ in range(int(next(it))): gives the next number: _ is 1.
  24. Line 8. n = int(next(it)) sets n to 4 (it was 5).
  25. Line 9. marks = [0] * n sets marks to [0, 0, 0, 0] (it was [40, 75, 90, 60, 35]).
  26. Line 10. for i in range(n): gives its first number: i is 0.
  27. Line 11. marks[i] = int(next(it)) changes marks: it is now [50, 0, 0, 0].
  28. Line 10. for i in range(n): gives the next number: i is 1.
  29. Line 11. marks[i] = int(next(it)) changes marks: it is now [50, 50, 0, 0].
  30. Line 10. for i in range(n): gives the next number: i is 2.
  31. Line 11. marks[i] = int(next(it)) changes marks: it is now [50, 50, 50, 0].
  32. Line 10. for i in range(n): gives the next number: i is 3.
  33. Line 11. marks[i] = int(next(it)) changes marks: it is now [50, 50, 50, 50].
  34. Line 10. for i in range(n): has no number left: a range stops before its stop value, 4, so the loop ends.
  35. Line 12. total = sum(marks) sets total to 200 (it was 300).
  36. Line 13. above = sum(1 for m in marks if m * n > total) sets above to 0 (it was 2).
  37. Line 14. out.append(str(above)) adds '0' to the end of out.
  38. Line 7. for _ in range(int(next(it))): gives the next number: _ is 2.
  39. Line 8. n = int(next(it)) sets n to 3 (it was 4).
  40. Line 9. marks = [0] * n sets marks to [0, 0, 0] (it was [50, 50, 50, 50]).
  41. Line 10. for i in range(n): gives its first number: i is 0.
  42. Line 11. marks[i] = int(next(it)) changes marks: it is now [10, 0, 0].
  43. Line 10. for i in range(n): gives the next number: i is 1.
  44. Line 11. marks[i] = int(next(it)) changes marks: it is now [10, 20, 0].
  45. Line 10. for i in range(n): gives the next number: i is 2.
  46. Line 11. marks[i] = int(next(it)) changes marks: it is now [10, 20, 31]. 31 stood on a line of its own in the input. In tokens it is just the next item, so the third class gets all three of its marks.
  47. Line 10. for i in range(n): has no number left: a range stops before its stop value, 3, so the loop ends.
  48. Line 12. total = sum(marks) sets total to 61 (it was 200).
  49. Line 13. above = sum(1 for m in marks if m * n > total) sets above to 1 (it was 0).
  50. Line 14. out.append(str(above)) adds '1' to the end of out.
  51. Line 7. for _ in range(int(next(it))): has no number left: a range stops before its stop value, 3, so the loop ends.
  52. Line 15. print("\n".join(out)) prints 3 lines, starting with 2. That was the last line, so the program ends. One read and one walk over the tokens gave 2, 0 and 1, the same answers that template.py prints for the same input.

The split turned the eight lines of input into one flat list of 16 tokens, and next(it) took them in order: the 5 said how many marks followed, and 31, alone on its line, arrived like any other token.

Reading sys.stdin.buffer gives bytes, not text. Numbers do not care, because int() and float() accept bytes, but comparing a token with a string does:

Tokens are bytes Python · bytes_tokens.py
# What sys.stdin.buffer.read() returns for this input: bytes, not str
raw = b"YES 42\n-7 3.5\nRavi Kumar\n"

tokens = raw.split()
print(tokens)
print(int(tokens[1]) + int(tokens[2]), float(tokens[3]))  # int() and float() accept bytes

print(tokens[0] == "YES")             # bytes never equal str: False
print(tokens[0] == b"YES")            # compare with a bytes literal
print(tokens[0].decode() == "YES")    # or decode the token first

lines = raw.splitlines()              # for a line with spaces in it
print(lines[2].decode())

Output

[b'YES', b'42', b'-7', b'3.5', b'Ravi', b'Kumar']
35 3.5
False
True
True
Ravi Kumar

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bytes_tokens.py

Comparing a bytes token with a str

tokens[0] == "YES" is False and raises no error, the quietest bug in the template. Compare with a bytes literal such as b"YES", or call .decode() on the tokens that are text, as the last two comparisons above do.

A line that contains spaces, such as a name, is split into several tokens; read such inputs line by line with splitlines() instead.

Checkpoint

Check: The read-once template and its tokens

  1. Question 1 of 2 In the traced walk, it has just handed over 35, the last mark of the first class. Which token does the next next(it) hand over?

    Choose one answer.

    A hint

    The tokens are one flat list in the order of the input; the flag moves one place per next(it).

    Show the answer: Question 1 of 2

    Answer: b'4', the number of marks of the second class

    split() turns the whole input into one list of tokens, so the line breaks no longer exist. After 3, 5 and the five marks of the first class, the next token is 4, the count of the second class.

  2. Question 2 of 2 What does this print?

    Read the code, then choose one answer.

    tokens = b"NO 7".split()
    print(tokens[0] == "NO", tokens[0] == b"NO")
    Show the answer: Question 2 of 2

    Answer: False True

    The tokens of sys.stdin.buffer are bytes, so they equal bytes literals such as b"NO", never strings. Compare with b"NO", or decode the token first.

Reading everything in one call is many times faster

How much does the way of reading matter? This program writes a file of 1,000,000 numbers, one per line, and runs four small programs on it, each in a fresh Python with the file on its standard input, exactly as a judge would. Each one adds the numbers up and reports how long its reading took:

Four ways to read a million numbers Python · read_speed.py
"""Reads 1,000,000 numbers from standard input four ways, each in a fresh Python, as a judge would run it.

Each way runs three times and the fastest run counts: slower runs are mostly other programs getting in the way."""
import os
import subprocess
import sys
import tempfile
import tracemalloc

N = 1_000_000
READERS = {
    "input() per line": "total = sum(int(input()) for _ in range(n))",
    "sys.stdin.readline() per line": "readline = sys.stdin.readline\ntotal = sum(int(readline()) for _ in range(n))",
    "sys.stdin.read().split()": "total = sum(map(int, sys.stdin.read().split()))",
    "sys.stdin.buffer.read().split()": "total = sum(map(int, sys.stdin.buffer.read().split()))",
}
# The child times only its reading and adding, then prints the sum and the milliseconds.
CHILD = """import sys, time
n = {n}
start = time.perf_counter()
{body}
print(total, round((time.perf_counter() - start) * 1000))
"""

with tempfile.TemporaryDirectory() as folder:
    path = os.path.join(folder, "input.txt")
    with open(path, "w") as f:
        f.write("\n".join(str(i * 7 % 1_000_003) for i in range(N)) + "\n")
    for name, body in READERS.items():
        runs = []
        for _ in range(3):
            with open(path, "rb") as stdin:
                run = subprocess.run(
                    [sys.executable, "-c", CHILD.format(n=N, body=body)],
                    stdin=stdin, capture_output=True, text=True, check=True,
                )
            runs.append(tuple(map(int, run.stdout.split())))
        total, ms = min(runs, key=lambda r: r[1])
        print(f"{name:32} sum {total:,} in {ms:>5,} ms")
    with open(path, "rb") as f:
        data = f.read()
    tracemalloc.start()
    tokens = data.split()
    print(f"memory for the {len(tokens):,} tokens of one split(): {tracemalloc.get_traced_memory()[0] / 1e6:.1f} MB")

Output

input() per line                 sum 499,999,500,036 in 2,053 ms
sys.stdin.readline() per line    sum 499,999,500,036 in   135 ms
sys.stdin.read().split()         sum 499,999,500,036 in    86 ms
sys.stdin.buffer.read().split()  sum 499,999,500,036 in    76 ms
memory for the 1,000,000 tokens of one split(): 47.4 MB

This output changes from run to run: it measures time, which differs from one run to the next and from one computer to another; the computer of this run is named below the output.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 read_speed.py

All four agree on the sum; they differ in time by a factor of about twenty or more. input() is built for a person at a keyboard: every call does work that a judge’s input does not need, from handling a possible prompt to raising two auditing events, before it reads one line, and a million calls of that extra work cost seconds. sys.stdin.readline() reads one line per call without that overhead, which is fine when the input really is line by line. Reading everything with read().split() is the fastest and the simplest, and sys.stdin.buffer skips decoding the bytes into text, which saves a little more.

The price is memory, shown on the last line: every token is a small bytes object, about 50 bytes each with its place in the list, and all of them exist at once. A few million numbers fit easily in a memory limit of 256 MB; for an input far larger than the memory limit, read it line by line instead.

Key idea

One sys.stdin.buffer.read().split() replaces a million calls of input(): the reading takes a fraction of the time, and the cost is every token held in memory at once.

Checkpoint

Check: Reading the whole input at once

  1. Question 1 of 2 Which way of reading a million numbers, one per line, was the fastest in the measured run?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: sys.stdin.buffer.read().split(), then int() on each token

    All four programs added the same numbers. Reading everything in one call was the fastest, and skipping the decoding with sys.stdin.buffer saved a little more; one input() per line was the slowest.

  2. Question 2 of 2 Each token of one split() costs about 50 bytes, its place in the list included. About how many megabytes does a split of 2,000,000 numbers hold at once?

    Type a number.

    A hint

    Multiply the number of tokens by the bytes per token.

    Show the answer: Question 2 of 2

    Answer: 100 MB (anything from 90 to 110 counts)

    2,000,000 tokens × about 50 bytes is about 100,000,000 bytes, so about 100 MB, all held at once. That fits a memory limit of 256 MB; an input much larger than the limit is read line by line instead.

Write the output once; flush only for an interactive judge

Output has the same problem in reverse: one print() per answer is a function call per line. This program writes 200,000 lines three ways, each into a file, which is where a judge’s output goes:

Three ways to write 200,000 lines Python · write_speed.py
"""Writes 200,000 answer lines three ways, each in a fresh Python with its output going to a file, as on a judge.

Each way runs three times and the fastest run counts."""
import os
import subprocess
import sys
import tempfile

N = 200_000
WRITERS = {
    "print() per line": "for i in range(n):\n    print(i)",
    "print(..., flush=True) per line": "for i in range(n):\n    print(i, flush=True)",
    "one join, one write": 'sys.stdout.write("\\n".join(map(str, range(n))) + "\\n")',
}
# The child writes its answers to standard output and its time, in milliseconds, to standard error.
CHILD = """import sys, time
n = {n}
start = time.perf_counter()
{body}
sys.stdout.flush()
print(round((time.perf_counter() - start) * 1000), file=sys.stderr)
"""

with tempfile.TemporaryDirectory() as folder:
    path = os.path.join(folder, "output.txt")
    for name, body in WRITERS.items():
        times = []
        for _ in range(3):
            with open(path, "w") as out:
                run = subprocess.run(
                    [sys.executable, "-c", CHILD.format(n=N, body=body)],
                    stdout=out, stderr=subprocess.PIPE, text=True, check=True,
                )
            times.append(int(run.stderr))
        print(f"{name:32} {os.path.getsize(path):,} bytes in {min(times):>5,} ms")

Output

print() per line                 1,288,890 bytes in    59 ms
print(..., flush=True) per line  1,288,890 bytes in 1,181 ms
one join, one write              1,288,890 bytes in    19 ms

This output changes from run to run: it measures time, which differs from one run to the next and from one computer to another; the computer of this run is named below the output.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 write_speed.py

All three files are the same size. Joining the lines and writing once is the fastest. Plain print() per line is slower but still reasonable, because when standard output is a file or a pipe, Python collects it in an output buffer: Memory where written text waits before it is sent on: a line at a time when standard output is a terminal, in larger blocks for a file or a pipe. print(..., flush=True) sends it at once. More in “Input and output: print(), input() and stdin” and writes it in large blocks. flush=True gives up that saving: every line becomes its own write to the operating system, and the same output takes many times as long. Step through what the buffer does with each line, first in an ordinary problem and then in an interactive one:

Interactive · message by message Lines in the output buffer, ordinary and interactive Step through what happens to each print(). The middle column is the buffer that sits between your program and the judge.
Lines in the output buffer, ordinary and interactive, step 6 of 6. Messages so far: solution.py to Output buffer: print(i), 200,000 times; Output buffer to Judge: large blocks of lines; solution.py to Output buffer: ? 50; Output buffer to Judge: ? 50; Judge to solution.py: higher.solution.pynext: ? 75, flushedOutput bufferemptyJudgeread "? 50"print(i), 200,000 timeslarge blocks of lines? 50? 50higher

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. Lines collect. When standard output is a file or a pipe, as on a judge, print() puts each line into the output buffer instead of handing it to the operating system at once.
  2. A few large writes. The buffer goes out in large blocks, when it fills and when the program ends. That is why plain print() stayed fast in the measurement above.
  3. An interactive problem. In an interactive problem the judge answers each query before the next one. The program prints its first guess, "? 50", and calls sys.stdin.readline() to read the reply.
  4. Both sides wait. The guess is still in the buffer, so the judge has not seen it and waits for a line. The program waits for the reply. Neither moves until the time limit ends the run.
  5. flush=True sends it. print("? 50", flush=True) empties the buffer straight away, so the line reaches the judge the moment it is printed.
  6. The reply comes back. The judge reads 50 and answers on the program's standard input. The program reads the reply and prints its next guess, flushed again, one line per query.

Note

interactive problem: A coding-round problem in which the judge reads each line the program prints and replies before the next one. The program must flush its output after every query, or both sides wait until the time limit ends the run. are the exception. There the judge reads each line you print and answers it before you continue, so a line left in the buffer leaves both programs waiting for each other until the time limit. In an interactive problem, use print(..., flush=True) (or sys.stdout.flush()) after every query; everywhere else, never flush line by line.

Checkpoint

Check: Writing once, flushing for interactive judges

  1. Question 1 of 2 Why did print(..., flush=True) per line take many times as long as plain print() for the same 200,000 lines?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Every flush sends one line to the operating system on its own, instead of a few large blocks

    When standard output is a file or a pipe, Python collects printed lines in a buffer and writes them in large blocks. flush=True empties the buffer after every line, so 200,000 lines become 200,000 writes.

  2. Question 2 of 2 In an interactive problem, your program prints a guess with print(guess) and then calls sys.stdin.readline() for the reply. The run ends with "time limit exceeded". Why?

    Choose one answer.

    A hint

    Where is the guess when readline() starts waiting?

    Show the answer: Question 2 of 2

    Answer: The guess stayed in the buffer, so the judge waited for it while the program waited for the reply

    An interactive judge answers each query before the next one, so it waits for the guess. The guess was still in the output buffer, and the program waited in readline(). print(guess, flush=True) sends it at once.

Recursion stops near 1,000 frames; a list of your own does not

Every unfinished call keeps a frame: The record of one function call that has not finished: its local variables and the place to carry on when the call it is waiting for returns. More in “Recursion, the call stack and its limits”, and CPython stops at a recursion limit: The most frames CPython allows on its call stack at once, 1,000 by default, counting the frames below your first call. Going past it raises RecursionError. More in “Recursion, the call stack and its limits” of 1,000 frames by default. That is plenty for most recursion, but not for a depth-first search over a graph shaped like a long path, because the search goes one frame deeper for every node:

A depth-first search 100,000 nodes deep Python · deep_dfs.py
import sys

N = 100_000
# A path 0 - 1 - 2 - ... - 99,999: the deepest graph a depth-first search can meet
graph = [[] for _ in range(N)]
for v in range(N - 1):
    graph[v].append(v + 1)
    graph[v + 1].append(v)


def dfs_recursive(v, seen):
    seen.add(v)
    for w in graph[v]:
        if w not in seen:
            dfs_recursive(w, seen)


def dfs_iterative(start):
    seen = {start}
    stack = [start]  # the nodes still to explore, in a list of our own
    while stack:
        v = stack.pop()
        for w in graph[v]:
            if w not in seen:
                seen.add(w)
                stack.append(w)
    return seen


print("recursion limit:", sys.getrecursionlimit())
seen = set()
try:
    dfs_recursive(0, seen)
    print(f"recursive DFS visited {len(seen):,} nodes")
except RecursionError as error:
    print(f"recursive DFS stopped with {type(error).__name__} after {len(seen):,} nodes")
print(f"iterative DFS visited {len(dfs_iterative(0)):,} nodes")

Output

recursion limit: 1000
recursive DFS stopped with RecursionError after 999 nodes
iterative DFS visited 100,000 nodes

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 deep_dfs.py

Version note

In the browser, the code that starts your program keeps a few frames of its own beneath it, so Run stops the recursive search a few nodes before 999. Those frames and the search’s still add up to the limit of 1,000.

In your browser, Pyodide 314.0.7 (CPython 3.14.2) prints:

recursion limit: 1000
recursive DFS stopped with RecursionError after 994 nodes
iterative DFS visited 100,000 nodes

Step through what the recursive search does to the call stack, and what the iterative search keeps instead:

Interactive · step by step dfs_recursive() against the limit, and a list instead of frames Step through deep_dfs.py. The left column is the call stack of the recursive search, the right column what the loop keeps instead.
dfs_recursive() against the limit, and a list instead of frames, step 7 of 7. Showing the module's code, stack, a list (empty: []), seen, a set (100,000 nodes). In focus: seen, a set.the module's codeframe 1stack, a listempty: []seen, a set100,000 nodes

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. The module's frame. Before any search starts, the module's own code is running in a frame. It is frame 1 of the 1,000 that sys.getrecursionlimit() reported.
  2. One frame per node. dfs_recursive(0) visits node 0 and calls itself on its neighbour 1, which calls itself on 2, and so on. None of them can return before the end of the path, so every node keeps a frame.
  3. The last frame allowed. dfs_recursive(998) runs in frame 1,000, the last one the limit allows. By now the search has visited nodes 0 to 998, which is 999 nodes.
  4. One call too many. Calling dfs_recursive(999) would need frame 1,001, so Python raises RecursionError instead. The program prints that it stopped after 999 nodes of 100,000.
  5. A list instead. dfs_iterative() keeps the nodes still to explore in a list of its own and takes the last one each time. A list lives in ordinary memory, so it has no limit like the call stack's.
  6. One node at a time. On this path the list never holds more than one node. The loop takes 0 and puts 1 on the list, then takes 1 and puts 2 on it, all inside the one call of dfs_iterative().
  7. Every node visited. The loop ends when the list is empty, with all 100,000 nodes in seen, and the program prints 100,000. The depth of the graph never mattered.

The iterative version keeps the nodes still to explore in a list of its own, an explicit stack: A list that a loop uses to keep the work still to do, in place of recursive calls: append() pushes an item and pop() takes the last one. Only memory limits its size. More in “Recursion, the call stack and its limits”, and takes the last one each time, as the call stack does, but without its limit. A list holds millions of items, so the depth no longer matters. The order in which neighbours are visited can differ from the recursive version, which rarely matters for a search; when it does, push the neighbours in reverse order and mark a node as seen when it is taken from the list, not when it is put on it.

Raising the limit is the other common answer, and it works only for some recursion. Since Python 3.11, most calls from Python code to a Python function use no C stack at all, so plain recursion can go much deeper once the limit is raised. A call that passes through C code, such as a function wrapped by functools.cache, still uses the C stack, which has a fixed size:

Raising the limit: what it fixes and what it does not Python · deep_limit.py
import functools
import sys
import threading

sys.setrecursionlimit(1_000_000)


def depth(n):
    return 0 if n == 0 else 1 + depth(n - 1)


@functools.cache
def cached_depth(n):
    return 0 if n == 0 else 1 + cached_depth(n - 1)


def attempt(f, n):
    try:
        return f"{f(n):,} levels"
    except RecursionError as error:
        return "RecursionError: " + str(error).split(" (")[0]


print("plain function, 100,000 deep: ", attempt(depth, 100_000))
print("cached function, 10,000 deep: ", attempt(cached_depth, 10_000))

# The workaround some judge solutions use: a thread with a much larger stack
threading.stack_size(512 * 1024 * 1024)
cached_depth.cache_clear()
result = []
worker = threading.Thread(target=lambda: result.append(attempt(cached_depth, 100_000)))
worker.start()
worker.join()
print("cached function, 100,000 deep, 512 MiB thread stack:", result[0])

Output

plain function, 100,000 deep:  100,000 levels
cached function, 10,000 deep:  RecursionError: Stack overflow
cached function, 100,000 deep, 512 MiB thread stack: 100,000 levels

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 deep_limit.py

With the limit at a million, the plain function went 100,000 levels deep, but the cached one failed on a recursion 10,000 levels deep: CPython 3.14 protects its C stack and raised RecursionError with the message “Stack overflow” instead of crashing. A thread created after threading.stack_size() gets a larger stack, and with 512 MiB the cached function reached 100,000 levels. That workaround depends on the platform and on how much memory the judge allows, so treat it as a last resort; the documentation of sys.setrecursionlimit() adds that a very high limit can make Python crash. The explicit stack works everywhere.

Key idea

When the depth of a recursion grows with the input, rewrite it as a loop over a list of your own: neither the 1,000-frame limit nor the C stack applies to a list.

Checkpoint

Check: Deep recursion and the explicit stack

  1. Question 1 of 2 The same recursive search runs on a path of only 600 nodes, with the default recursion limit of 1,000. What happens?

    Choose one answer.

    A hint

    Count the frames, the module's own included.

    Show the answer: Question 1 of 2

    Answer: It visits all 600 nodes, using 601 frames

    The limit counts frames: the module's frame plus one per unfinished call. 600 nodes need 601 frames, well under 1,000, so the search finishes. Only depths near the limit fail.

  2. Question 2 of 2 A teammate raises the limit to 1,000,000 and wraps a recursive function in functools.cache. In the measured run, what happened at 10,000 levels?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: RecursionError with the message "Stack overflow", because the cached calls use the C stack

    Since Python 3.11 most Python-to-Python calls use no C stack, so a raised limit lets plain recursion go deep. A call through C code, such as a functools.cache wrapper, still uses the C stack, and a recursion 10,000 levels deep overflowed it. A loop with an explicit stack has neither limit.

Python ints never overflow, but their decimal text stops at 4,300 digits

Python’s integers grow as large as memory allows, so sums and products never overflow as they can in C++ or Java. Two limits remain:

A 6,021-digit number Python · big_ints.py
import math
import sys

E = 20_000  # the exponent: try 14_284, then 14_285
x = 2 ** E  # Python ints never overflow
print("bits:", x.bit_length())
print("decimal digits:", math.floor(math.log10(x)) + 1)

try:
    print(str(x))
except ValueError as error:
    print("ValueError:", error)

print("hex digits:", len(hex(x)) - 2)  # power-of-two bases have no limit

sys.set_int_max_str_digits(0)  # 0 removes the limit, for this program only
print("first digits:", str(x)[:12])

MOD = 10**9 + 7  # judges often ask for the answer modulo a prime like this
print(f"2 ** {E} % MOD =", x % MOD, "=", pow(2, E, MOD))

Output

bits: 20001
decimal digits: 6021
ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit
hex digits: 5001
first digits: 398027684033
2 ** 20000 % MOD = 614428880 = 614428880

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 big_ints.py

  • Text conversion. Turning an int into decimal text, or decimal text into an int, is not a linear-time job for huge numbers, so CPython refuses numbers of more than 4,300 decimal digits by default, a protection against inputs that would keep a server busy. hex(), bin() and oct() have no limit, and sys.set_int_max_str_digits(0) removes it for one program when a problem really asks you to print a huge number.
  • Speed. Arithmetic on a number with thousands of digits takes far longer than on a small one. When a problem asks for the answer “modulo 10⁹ + 7”, it wants you to reduce after every step so that the numbers stay small: (a * b) % MOD in a loop, or pow(a, b, MOD), which computes a modular power without ever building the huge number. The example checks that both ways give the same remainder.

Find the edge of the limit

Press Edit, change E = 20_000 to E = 14_284 and run: that power of 2 has exactly 4,300 digits, and str() prints it in full. Change it to 14_285, one digit more, and the ValueError is back.

Checkpoint

Check: Big ints, the digit limit and modular answers

  1. Question 1 of 2 x = 2 ** 20_000 has 6,021 decimal digits. Under the default limit, which of these works on it?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: hex(x)

    CPython refuses decimal conversions of more than 4,300 digits by default. hex(), bin() and oct() are not limited, and sys.set_int_max_str_digits(0) removes the limit when a problem really asks for the decimal digits.

  2. Question 2 of 2 The answer is 3 to the power 10¹⁸, modulo 1,000,000,007. Which line computes it within a time limit?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: pow(3, 10**18, MOD)

    pow(a, b, m) computes a modular power by repeated squaring, reducing after every step, so 10¹⁸ needs only about 60 squarings of small numbers. It is the tool for every "modulo 10⁹ + 7" power.

Check which Python the judge runs before you use a new feature

Judges list the language versions they offer, and code that uses a feature newer than the judge’s Python fails there with a runtime error, even though it works at home. The first submission to a new judge can print the version to standard error, and features can be checked at run time:

The version and three features that need a recent Python Python · version_check.py
import heapq
import itertools
import math
import sys

print("implementation:", sys.implementation.name)
print("version:", "%d.%d" % sys.version_info[:2])
print("math.lcm, Python 3.9+:", hasattr(math, "lcm"))
print("itertools.batched, Python 3.12+:", hasattr(itertools, "batched"))
print("heapq.heappush_max, Python 3.14+:", hasattr(heapq, "heappush_max"))

Output

implementation: cpython
version: 3.14
math.lcm, Python 3.9+: True
itertools.batched, Python 3.12+: True
heapq.heappush_max, Python 3.14+: True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 version_check.py

math.lcm() needs Python 3.9, itertools.batched() 3.12 and heapq.heappush_max() 3.14. Walk through the versions that added the features solutions use most:

Timeline · 6 events in order Features a solution may need, by the Python that added them Walk through the versions in order. A judge whose Python is older than a feature's version cannot run code that uses it.
  1. 3.8

    math.isqrt(), math.comb() and modular inverses new functions

    math.isqrt() gives exact integer square roots without floats, math.comb() and math.perm() count choices, and pow(a, -1, m) computes a modular inverse.

  2. 3.9

    math.lcm(), and gcd() of many numbers new functions

    math.lcm() is added, and math.gcd() accepts any number of arguments instead of exactly two.

  3. 3.10

    bisect with key, zip() with strict new parameters

    The bisect functions accept a key function, zip(..., strict=True) insists on inputs of equal length, and int.bit_count() counts the ones in a number's binary form.

  4. 3.11

    Faster code, and calls without C stack faster

    CPython 3.11 is about 25 % faster than 3.10 on its benchmark suite, and most calls between Python functions use no C stack, so plain recursion can go deeper once the limit is raised.

  5. 3.12

    itertools.batched() new function

    itertools.batched() cuts an iterable into tuples of n items, the last one possibly shorter. 3.12 is also the newest language version that PyPy supports.

  6. 3.14

    Max-heaps in heapq new functions

    heapq gains heapify_max(), heappush_max(), heappop_max(), heapreplace_max() and heappushpop_max(), functions for heaps that keep the largest item first.

The timeline has 6 events, from 3.8 to 3.14.

Some judges also offer PyPy: A separate implementation of the Python language with a just-in-time compiler, which turns code that runs often into machine code while the program runs. It implements an older version of the language than the newest CPython., a separate implementation of Python with a just-in-time compiler. Its project reports that, on average, PyPy 3.12 runs about four times as fast as CPython 3.11, and it supports the language of Python 3.12 and 3.11, so features added in 3.13 or 3.14 are missing there. Submit to PyPy when a correct solution is close to the time limit and uses nothing newer than its version.

Checkpoint

Check: The judge's Python version

  1. Question 1 of 2 A solution calls itertools.batched(). The judge runs CPython 3.11. What happens when it runs?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: It stops with AttributeError, so the test gets a runtime error

    A name a module does not have is an AttributeError when the line runs. Check the judge's version, or test with hasattr(itertools, "batched") and fall back to a slice in a loop.

  2. Question 2 of 2 A correct solution takes 1.8 s of a 2 s limit, mostly in a Python loop, and uses math.lcm() and zip(..., strict=True). The judge offers CPython 3.13 and PyPy 3.11. What do you try first?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Submit it under PyPy 3.11, which has both features and runs loops faster

    PyPy suits a correct solution near the time limit whose time goes into Python loops, as long as it uses nothing newer than PyPy's language version. Both features here predate 3.11.

Practise: three judge-style problems at full size

Practise with problems that are original or openly licensed, such as the ones in this track and the Data Structures & Algorithms (DSA) and coding-patterns tracks of MySmartCoPilot. The questions of companies’ online assessments belong to those companies, and MySmartCoPilot never republishes them; a question that “was asked” somewhere teaches nothing that an original problem of the same pattern does not. Before you submit any solution, run six checks:

  1. Run the largest input. Generate one at the limits of the statement with a few lines of Python, and time the whole program on it, reading and writing included.
  2. Clean standard output. No prompts, no debugging lines: only the answers, in the order asked.
  3. Match the format exactly. One answer per line unless told otherwise, the exact spelling of words such as YES, and the number of decimal places the statement asks for.
  4. Remove deep recursion. Any recursion whose depth grows with the input becomes a loop with a stack.
  5. Reduce as you go. When the answer is asked modulo a prime, take the remainder after every step.
  6. Check the version. Use only what the judge’s Python, or PyPy, provides.

The exercise has three problems at the sizes a judge would use. Each function receives the whole input as bytes and returns the whole output, and the sample tests include inputs at the limits:

Exercise · Medium · Python

Three judge-style problems at full input size

Write three functions in judge.py. Each one receives the whole input of a test as bytes, exactly what sys.stdin.buffer.read() returns on a judge, and returns the whole output as one string, with one answer per line. A main() that reads standard input and writes your result would be the only thing left to add before submitting. Spaces at the ends of lines and a final line break do not matter.

  1. range_sums(data). The first line holds n and q, the second line n whole numbers a1 … an, and each of the next q lines a pair l r with 1 ≤ l ≤ r ≤ n. Print the sum a_l + … + a_r for each pair. Up to 200,000 numbers and 200,000 pairs, each number between -10⁹ and 10⁹.
  2. frequency_of_frequencies(data). The first token is n, followed by n words of small letters, separated by spaces or line breaks. For every count c that some word has, in increasing order of c, print c k: k words occur exactly c times. Up to 200,000 words.
  3. grid_steps(data). The first line holds r and c, then r lines of c characters: . is open, # is a wall, S is the start and E the exit, each exactly once. Print the least number of moves from S to E, moving up, down, left or right through cells that are not walls, or -1 if the exit cannot be reached. Up to 300 rows and 300 columns.

The sample tests include inputs at those limits and must finish within the time limit of the Run button, so a solution that is slower than about n log n for any of the three will not pass.

Starter code · judge.py

from collections import Counter, deque


def range_sums(data):
    """The sum of a[l..r] for every pair l r, one per line."""
    return ""


def frequency_of_frequencies(data):
    """Lines "c k": k distinct words occur exactly c times, by increasing c."""
    return ""


def grid_steps(data):
    """The least number of moves from S to E, or -1."""
    return ""
The sample tests · test_judge.py
from judge import frequency_of_frequencies, grid_steps, range_sums


def lines(text):
    """The output's lines, without spaces at their ends or blank lines at the end."""
    return [line.rstrip() for line in str(text).strip().splitlines()]


def letters(i):
    """A different word of small letters for every number i."""
    word = ""
    while True:
        i, r = divmod(i, 26)
        word += "abcdefghijklmnopqrstuvwxyz"[r]
        if i == 0:
            return word


def test_range_sums_sample():
    """answers each pair with the sum of its range"""
    data = b"5 4\n3 -1 4 1 5\n1 5\n2 3\n4 4\n1 1\n"
    assert lines(range_sums(data)) == ["12", "3", "1", "3"]


def test_range_sums_at_the_limits():
    """answers 200,000 pairs over 200,000 numbers in time"""
    n = 200_000
    numbers = " ".join(str(i if i % 2 else -i) for i in range(1, n + 1))  # 1 -2 3 -4 ...
    pairs = [(i, n - i + 1) for i in range(1, n // 2 + 1)] + [(i, i) for i in range(1, n // 2 + 1)]

    def first(k):  # 1 - 2 + 3 - 4 ... up to the k-th number
        return (k + 1) // 2 if k % 2 else -(k // 2)

    text = f"{n} {len(pairs)}\n{numbers}\n" + "\n".join(f"{l} {r}" for l, r in pairs) + "\n"
    out = lines(range_sums(text.encode()))
    assert len(out) == len(pairs)
    assert out[:3] == ["-100000", "99999", "-99998"]
    assert out == [str(first(r) - first(l - 1)) for l, r in pairs]


def test_frequency_sample():
    """counts how many words share each count"""
    data = b"9\ntea coffee tea\nmilk tea water\ncoffee milk juice\n"
    assert lines(frequency_of_frequencies(data)) == ["1 2", "2 2", "3 1"]


def test_frequency_at_the_limits():
    """handles 200,000 words in time"""
    words = []
    for i in range(1, 501):  # one word that occurs once, one twice ... one 500 times
        words += ["w" + letters(i)] * i
    words += ["z" + letters(i) for i in range(74_750)]  # and 74,750 words that occur once
    assert len(words) == 200_000
    out = lines(frequency_of_frequencies(f"{len(words)}\n{' '.join(words)}\n".encode()))
    assert out == ["1 74751"] + [f"{c} 1" for c in range(2, 501)]


def test_grid_sample():
    """finds the shortest way round the walls"""
    data = b"4 5\nS..#.\n.#.#.\n.#...\n...#E\n"
    assert lines(grid_steps(data)) == ["7"]


def test_grid_all_directions():
    """moves up and left as well as down and right"""
    assert lines(grid_steps(b"3 5\nS#.#E\n.#.#.\n.....\n")) == ["8"]
    assert lines(grid_steps(b"3 5\nE#.#S\n.#.#.\n.....\n")) == ["8"]


def test_grid_unreachable():
    """prints -1 when the exit is walled off"""
    data = b"3 3\nS#.\n##.\n..E\n"
    assert lines(grid_steps(data)) == ["-1"]


def test_grid_at_the_limits():
    """searches a 300 x 300 grid in time"""
    rows = ["." * 300 for _ in range(300)]
    rows[0] = "S" + rows[0][1:]
    rows[-1] = rows[-1][:-1] + "E"
    assert lines(grid_steps(("300 300\n" + "\n".join(rows) + "\n").encode())) == ["598"]
A hint

Split the bytes once with data.split() and walk through the tokens with an iterator, converting with int() only the tokens that are numbers. For range_sums, build the prefix sums first, with prefix[i] the sum of the first i numbers, so that every pair is answered by one subtraction, prefix[r] - prefix[l - 1]. For frequency_of_frequencies, count twice: Counter of the words, then Counter of those counts. For grid_steps, the grid lines are tokens too (they contain no spaces); search breadth first from S with a collections.deque, so that the first time you reach a cell is along a shortest path.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Python Online Compiler Paste a solution and its whole test input into the Input box, then press Run: the program reads it from standard input, as on a judge. Text Diff Compare what your program printed with the expected output, to find the one line that differs in a long answer.

Last, test the whole lesson on yourself. The practice below opens with whichever idea you have answered least well, and every answer is explained straight away:

Practice that adapts to you

Practise this lesson

15 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 15 What does a judge compare with a test's expected answer?

    Choose one answer.

    Show the answer: Question 1 of 15

    Answer: Everything the program printed on standard output

    A judge reads the program's standard output and compares it with the expected answer, often ignoring spaces at line ends and the final line break. Standard error is not compared.

  2. Question 2 of 15 A solution passes the 3 sample tests and then gets "time limit exceeded" on test 14 of 20. What should you do first?

    Choose one answer.

    Show the answer: Question 2 of 15

    Answer: Generate an input at the limits of the statement and time the whole program on it

    The samples are small, and the hidden tests grow towards the limits in the statement. Build an input of the largest size, time each phase, and fix the slowest one before you resubmit.

  3. Question 3 of 15 What does template.py print for the input shown with it?

    What does this program print? Choose one answer.

    import sys
    
    
    def solve(tokens):
        """Answers every test case: how many marks are above the class average."""
        it = iter(tokens)
        out = []
        for _ in range(int(next(it))):
            n = int(next(it))
            marks = [int(next(it)) for _ in range(n)]
            total = sum(marks)
            # m > total / n, compared without floats: m * n > total
            out.append(str(sum(1 for m in marks if m * n > total)))
        return out
    
    
    def main():
        tokens = sys.stdin.buffer.read().split()
        sys.stdout.write("\n".join(solve(tokens)) + "\n")
    
    
    if __name__ == "__main__":
        main()
    Show the answer: Question 3 of 15

    Answer: it prints

    2
    0
    1

    The first class averages 60, and 75 and 90 are above it. In the second every mark equals the average of 50, so none is above it. The third has marks 10, 20 and 31, an average of 61 ÷ 3, about 20.3: only 31 is above it. Comparing m * n > total gives these answers without any floating-point division.

  4. Question 4 of 15 Why does the template's solve() return a list of strings instead of printing the answers itself?

    Choose one answer.

    Show the answer: Question 4 of 15

    Answer: A test can call it and compare the result, and main() writes everything in one call

    solve() takes tokens and returns the answers, so a test can import it and check the list. main() turns that list into one text with join() and writes it with one call.

  5. Question 5 of 15 What does this print?

    Read the code, then choose one answer.

    tokens = b"YES 3".split()
    print(tokens[0] == "YES", int(tokens[1]) * 2)
    Show the answer: Question 5 of 15

    Answer: False 6

    Splitting bytes gives bytes, and a bytes object never equals a str, so the comparison is False without any error. int() accepts bytes that hold a number, so int(b"3") * 2 is 6. Compare with b"YES", or decode the token first.

  6. Question 6 of 15 A solution reads 1,000,000 numbers, one per line, with int(input()) in a loop, and its algorithm is O(n). It exceeds a 2-second limit. Which change helps most?

    Choose one answer.

    Show the answer: Question 6 of 15

    Answer: Read everything at once with sys.stdin.buffer.read().split() and convert the tokens with int()

    In the measured run, input() took seconds for a million lines while one read().split() took about a tenth of a second. Flushing every line makes output slower, not faster, and the recursion limit has nothing to do with reading.

  7. Question 7 of 15 Each of 2,000,000 input lines holds a full name with spaces, such as "Ravi Kumar", and the memory limit is 64 MB. How do you read the names?

    Choose one answer.

    Show the answer: Question 7 of 15

    Answer: Line by line, with sys.stdin.buffer.readline() or by looping over sys.stdin.buffer

    read().split() is the default because it is fast and ignores line structure. When a line carries spaces that matter and the input is large next to the memory limit, read one line at a time instead.

  8. Question 8 of 15 In which kind of problem must you flush standard output after each line you print?

    Choose one answer.

    Show the answer: Question 8 of 15

    Answer: An interactive problem, where the judge reads each line and answers it before your next question

    In an interactive problem the judge waits for your line before it replies, so a line left in the buffer leaves both programs waiting. For ordinary problems the judge reads the output after the program ends, and flushing every line only slows the program down: in the measured run, it made 200,000 lines take about twenty times as long.

  9. Question 9 of 15 A solution has 200,000 answers in a list of strings, lines. Which way of writing them was the fastest in the measured run?

    Choose one answer.

    Show the answer: Question 9 of 15

    Answer: sys.stdout.write("\n".join(lines) + "\n")

    Joining the answers and writing once was the fastest of the three ways, plain print() per line came second, and flushing every line was many times slower.

  10. Question 10 of 15 What does sys.getrecursionlimit() return in CPython 3.14 by default?

    Type a number.

    Show the answer: Question 10 of 15

    Answer: 1000 frames

    The default limit is 1,000 frames, as the first line of deep_dfs.py printed. The module's own frame counts too, so a recursion that goes one frame deeper per node stops just before node 999 of a long path.

  11. Question 11 of 15 A recursive depth-first search raises RecursionError on a path-shaped graph of 100,000 nodes. Which fix works on any judge, whatever its stack size?

    Choose one answer.

    Show the answer: Question 11 of 15

    Answer: Rewrite the search with a loop and a list used as a stack

    An explicit stack lives in ordinary memory, so its depth is limited only by memory. Catching the error gives a wrong answer, a cache saves repeated work but not depth (in the measured run a cached function overflowed the C stack on a recursion 10,000 levels deep, even with a raised limit), and a huge limit can let the interpreter run out of real stack instead.

  12. Question 12 of 15 By default, how many decimal digits can str() produce from an int in CPython 3.14 before it raises ValueError?

    Type a number.

    Show the answer: Question 12 of 15

    Answer: 4300 digits

    The limit is 4,300 digits (sys.int_info.default_max_str_digits). It guards against slow conversions in bases that are not powers of two; hex() and bin() are not limited, and sys.set_int_max_str_digits(0) removes the limit for one program.

  13. Question 13 of 15 A solution must print the product of 100,000 numbers modulo 1,000,000,007. Which loop body keeps it fast?

    Choose one answer.

    Show the answer: Question 13 of 15

    Answer: p = p * x % MOD

    Taking the remainder after every step keeps the numbers small and the answer exact, because (a × b) mod m equals ((a mod m) × b) mod m. Multiplying first and reducing at the end builds a huge number on the way.

  14. Question 14 of 15 Which of these needs Python 3.14 or newer?

    Choose one answer.

    Show the answer: Question 14 of 15

    Answer: heapq.heappush_max()

    version_check.py tests three of these at run time: math.lcm() needs 3.9, itertools.batched() 3.12 and heapq.heappush_max() 3.14. zip() gained strict in 3.10.

  15. Question 15 of 15 A solution should use itertools.batched() where the judge has it, and still run on an older Python. Which test does that?

    Choose one answer.

    Show the answer: Question 15 of 15

    Answer: if hasattr(itertools, "batched"):

    hasattr(module, "name") checks for a feature without touching it. sys.version_info compares as a tuple, such as sys.version_info >= (3, 12), when you need the version itself.

Interview questions

Fresher · Why do coding-round solutions read sys.stdin.buffer instead of calling input() in a loop?

Because the input of a judge’s test arrives all at once, and reading it in one call is both faster and simpler. input() is designed for a person typing: every call can write a prompt, raises auditing events and returns one line as text, so a million calls add up to seconds, while one sys.stdin.buffer.read().split() reads the same million numbers in about a tenth of a second. Splitting on any whitespace also stops caring how the numbers are spread over lines. buffer skips decoding bytes to text; the tokens are bytes, which int() accepts, but compare them with bytes literals or decode them first. The cost is memory, since every token is held at once, which is fine for typical limits. For line-structured input, sys.stdin.readline is a good middle way.

Key takeaways

  • A judge feeds each test on standard input and compares everything you print, so prompts and debugging lines stay off standard output, and one failing test fails the whole submission.
  • Read the whole input with sys.stdin.buffer.read().split() and walk the tokens with next(it): the line breaks stop mattering, the reading is many times faster, and the tokens are bytes.
  • Collect the answers and write them with one sys.stdout.write("\n".join(lines)); flush line by line only in interactive problems, where the judge waits for each query.
  • Recursion stops near 1,000 frames by default, and raising the limit does not help recursion through C code; a loop with a list of your own works on any judge.
  • int never overflows, but decimal text conversion stops at 4,300 digits by default, and modular answers want % MOD after every step or pow(a, b, MOD).
  • A feature added in a recent Python fails on a judge with an older one, so check the judge’s version, or PyPy’s, before you rely on it.

Terms in this lesson

interactive problem
A coding-round problem in which the judge reads each line the program prints and replies before the next one. The program must flush its output after every query, or both sides wait until the time limit ends the run.
online judge
A service that runs a submitted program on hidden tests: it gives each test's input on standard input, compares everything the program prints with the expected answer, and gives a verdict for every test, under a time limit and a memory limit.
PyPy
A separate implementation of the Python language with a just-in-time compiler, which turns code that runs often into machine code while the program runs. It implements an older version of the language than the newest CPython.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.