Input & Output
Input and output (I/O) is how a program communicates with the outside world — a user, a file, a network, another program. Almost every useful program does I/O somewhere; a program that only computes internally with no observable output is rarely useful in practice.
Input is data a program receives from outside itself (keyboard input, a file's contents, a network request). Output is data a program sends outward (printing to a screen, writing to a file, sending a network response).
Without I/O, a program would run in complete isolation, unable to react to anything or show anyone a result — I/O is what connects a program's internal computation to the real world and the people or systems using it.
Reading a form field on a website, printing a receipt at checkout, saving a document to disk, a sensor sending temperature data to a microcontroller, an API receiving a request over the network — all of these are I/O.
Think of a program as a person in a room with a small mail slot in the door. Input is mail coming in through the slot (instructions or data from outside); output is mail the person sends back out through the same slot. Whatever thinking happens inside the room stays invisible until it's expressed as outgoing mail.
input() pauses the program and waits for the user to type something on the keyboard, returning it as a string. print() writes text to the standard output, which usually appears in the terminal.
Standard streams — stdin (standard input), stdout (standard output), and stderr (standard error) — are a near-universal convention across operating systems and languages, letting programs be chained together (one program's stdout feeding another's stdin) without either program needing to know about the other specifically.
File I/O typically involves opening a file (getting a handle/descriptor from the OS), reading or writing data through that handle, and closing it afterward to release the underlying OS resource — forgetting to close a file can leak resources or leave data not fully written (buffered but not flushed to disk).
I/O operations are, relative to CPU computation, extremely slow — reading from a disk or network can take orders of magnitude longer than an equivalent amount of pure computation, which is why I/O-heavy programs are often designed around asynchronous or non-blocking I/O models (fully explored in the future Async Programming module) rather than simply waiting synchronously.
Buffering means I/O operations often don't happen immediately, byte by byte — data is accumulated in memory and flushed out (or read in) in larger chunks for efficiency, which is why explicitly flushing a stream is sometimes necessary to guarantee output has actually left the program.
Forgetting to close a file handle (or not using a language construct that closes it automatically, like Python's with statement or C++'s RAII), leaking OS resources or losing buffered but unflushed data.
Assuming input will always arrive in exactly the expected format, without validating or handling malformed input — a very common source of crashes and, in security-sensitive contexts, vulnerabilities.
Treating I/O as instantaneous in performance-sensitive code, and being surprised when a program handling many files or network requests becomes slow due to synchronous, blocking I/O calls.
Mixing up stdout and stderr's purposes — diagnostic/error messages generally belong on stderr, not stdout, so that a program's actual data output can still be piped cleanly to another program without error noise mixed in.
Using a context manager (Python's with open(...) as f:) or equivalent RAII pattern (C++) to guarantee a file is closed even if an error occurs while reading or writing it.
Redirecting a program's stdout to a file, or piping it into another program's stdin, using standard shell operators (> and |) — this works precisely because of the standard-stream convention.
Designing a network service around asynchronous I/O so a single process can handle many concurrent slow connections without dedicating a whole thread (and its overhead) to each one that's mostly just waiting.
System calls underneath I/O At the lowest level, virtually all I/O eventually goes through the operating system via system calls (read(), write(), open() and their equivalents) — a user-space program cannot directly touch a disk or network card; it asks the kernel to do so on its behalf. This boundary, and the associated context-switch cost, is covered in depth in the future Operating System Connection module.
Blocking vs. non-blocking vs. asynchronous I/O Blocking I/O halts the calling thread until the operation completes. Non-blocking I/O returns immediately with whatever is currently available (or an indication that nothing is ready yet), requiring the caller to poll or retry. Asynchronous I/O lets the caller register interest and continue doing other work, being notified (via a callback, event, or awaited future) when the operation eventually completes — the model underlying modern event loops, covered fully in the future Async Programming module.
Buffering strategies and performance Choosing a buffer size for file or network I/O is a real performance trade-off: too small, and the overhead of many small system calls dominates; too large, and memory usage grows and latency for small pieces of data can increase. Production I/O libraries typically tune buffering adaptively or expose it as a configurable parameter for exactly this reason.
What are stdin, stdout, and stderr?
The three standard I/O streams nearly every process gets by default: stdin for input (typically the keyboard, or piped data), stdout for normal program output, and stderr for error/diagnostic messages — kept separate so a program's real output and its error noise can be handled or redirected independently.
Why is it important to close a file after writing to it?
Because writes are often buffered — data may sit in memory rather than being written to disk immediately. Closing the file (or explicitly flushing it) ensures buffered data is actually written out and releases the OS-level file handle, avoiding resource leaks and data loss.
What's the difference between blocking and non-blocking I/O?
Blocking I/O pauses the calling thread until the operation finishes. Non-blocking I/O returns immediately, either with the available result or an indication that the operation isn't ready yet, letting the caller do other work in the meantime rather than waiting idle.
name = input("What is your name? ") # input
print("Hello, " + name + "!") # output