print() — more options than you thought
print() is the first function every Python programmer meets, and most never look past print("hello"). It has four optional keyword arguments worth knowing:
sep— the separator between multiple arguments. Default is a single space.print("a", "b", sep="|")printsa|b.end— what gets appended after the output. Default is"\n"(newline).print("hi", end="")printshiwith no trailing newline.file— where to write. Default issys.stdout. Set tosys.stderrfor error messages, or to any open file object.flush— force the output to appear immediately. Useful for progress bars and piped output.
input() — reading a line from the user
input(prompt) shows the prompt, waits for the user to type a line, and returns whatever they typed as a string. Always a string — if you need a number, you have to convert: int(input("age: ")).
If standard input reaches end-of-file (Ctrl-D on Unix, Ctrl-Z + Enter on Windows), input() raises EOFError. Wrap in try/except if your script might be piped input that runs out: try: line = input() except EOFError: ....
sys.argv — your first command-line interface
When you run python myscript.py foo bar baz, the values ["myscript.py", "foo", "bar", "baz"] end up in sys.argv. sys.argv[0] is always the script name; sys.argv[1:] is the actual arguments.
For one-off scripts, this is enough. For real CLI tools — argument parsing, help text, type coercion, subcommands — you'll want argparse or typer (covered in the CLI track). But knowing sys.argv directly is foundational; every higher-level CLI library is built on top.
script vs module — the __name__ == "__main__" idiom
Every Python file has a built-in variable __name__. When you run the file directly (python myscript.py), __name__ is the string "__main__". When the file is imported by another module, __name__ is the module's name (the filename without .py).
This enables a critical idiom — the if __name__ == "__main__": block. Code inside it runs only when the file is executed directly, not when it's imported. This lets you write a file that's both a usable script and an importable library, without weird side effects on import.
if __name__ == "__main__":. The functions and classes can sit at module top level (so they're importable). The actual run-this-thing logic gets gated. This costs nothing and saves you the day someone (or a future you) wants to import a function from your script.
The three standard streams — stdin, stdout, stderr
Every running program has three default streams, available as sys.stdin, sys.stdout, sys.stderr. print() writes to stdout by default. input() reads from stdin. Errors and warnings should go to stderr — that way pipes and redirects don't mix them with normal output.
This matters because Unix pipes only carry stdout. python myscript.py | grep hello grep's only the stdout. Errors go to stderr and stay visible on the terminal even when output is piped or redirected to a file.
scripts/) all follow the same pattern — normal output to stdout, status messages and warnings to stderr, exit code 0 for success and non-zero for failure. This lets the heartbeat scheduler tell, automatically, whether a job ran cleanly. Without that discipline the scheduler would have to parse logs to know what worked.
Running scripts — the four ways
Once you have a Python file, four common ways to run it:
python myscript.py— explicit, works everywherepython -m mymodule— runs a module as a script (uses Python's import machinery)./myscript.py— needs a shebang line (#!/usr/bin/env python3) at the top and the file marked executable (chmod +x myscript.py). Unix/macOS only.python -c "print('hello')"— runs an inline command without a file. Useful for one-liners and Makefile snippets.
Bottom line
You can now write a Python script that takes command-line arguments, reads from stdin, writes to stdout and stderr appropriately, and is also importable as a library. That's the shape of a real Python program. Lesson 8 onwards builds on top of that shape.
if __name__ == "__main__": for script entry points. Always send errors to stderr (print(..., file=sys.stderr)). Always exit with sys.exit(1) on failure, not just return. Tools that respect Unix conventions compose with other tools; tools that don't become islands.