Python Inputs Tutorial 2026 – input(), Validation & CLI | Poetic Bytes
Master Python input handling: input(), type conversion, validation, argparse, secure passwords, and real-world CLI patterns with runnable code.
Everything you need to handle user input in Python: the input() function, type conversion, validation, command-line arguments, secure input, and real-world patterns. 24 interactive lessons with runnable code — crafted by @PoeticGlenn.
What you will learn
How to capture, convert, validate, and securely handle user input in Python. Build interactive CLI tools, robust scripts, and production-ready applications that handle real-world data gracefully — all with step‑by‑step code explanations from @PoeticGlenn.
Why Mastering Python Input Handling Changes Everything
Every program that interacts with a human — or with another system — begins with input.
Whether you're building a command-line utility, a web API, a data pipeline, or a machine learning
model, the way you handle input determines the reliability, security, and usability of your entire
application. @PoeticGlenn designed this guide to take
you from the simplest input() call to production‑grade CLI tools with argparse,
all while teaching you the principles that separate amateur scripts from professional software.
Security First
Learn to validate, sanitise, and handle input safely — preventing injection, crashes, and data corruption.
Great UX
Build interactive tools that guide users, provide clear feedback, and never crash on bad input.
Production Ready
From sys.argv to argparse subcommands — write CLI tools that professionals love.
This isn't just a tutorial — it's a complete mental model for thinking about input in Python. Every concept is reinforced with runnable code, step‑by‑step breakdowns, and real‑world patterns you'll use every day. 🐍
Every Programmer Must Master Input Handling — Here's Why
If you write code that anyone will ever use — including your future self — you must understand how to handle input correctly. This curriculum, curated by @PoeticGlenn, is not optional; it's foundational. Here's why every developer, regardless of specialisation, needs these skills.
🔐 Security Is Non‑Negotiable
Unvalidated input is the #1 cause of vulnerabilities. From SQL injection to buffer overflows, nearly every exploit starts with unsanitised user data. This curriculum teaches you to validate, sanitise, and trust nothing — a mindset that protects every system you build.
🧠 Universal Skill, Every Domain
Whether you do web development, data science, DevOps, embedded systems, or AI — you will
handle input. input(), sys.argv, file I/O, environment variables —
this curriculum gives you the universal vocabulary to work across stacks.
💬 User Experience Matters
Programs that crash on bad input frustrate users and damage trust. Learning to loop until valid, provide clear error messages, and guide the user transforms a fragile script into a professional tool that people enjoy using.
⚙️ CLI Tools Are Everywhere
From automation scripts to deployment pipelines, command-line tools are the backbone of
modern development. With argparse and sys.argv, you'll build
composable, documented, and robust CLI applications that integrate
seamlessly with other tools.
Why Input Handling Is a Cornerstone of Professional Programming
In the broader programming ecosystem, input handling is not a "nice‑to‑have" — it's a fundamental pillar that separates hobbyist code from enterprise‑grade software. This curriculum, designed by @PoeticGlenn, reflects the patterns and principles used in millions of production systems worldwide.
Data Integrity
Every data pipeline, ETL job, and analytics workflow starts with input validation. Garbage in, garbage out — this curriculum teaches you to guarantee data quality from the first line.
API & Microservices
Modern architectures rely on clean, validated input at every boundary. The same principles you learn here — type checking, range validation, parsing — are directly applicable to REST APIs, GraphQL, and gRPC.
DevOps & Automation
CI/CD pipelines, infrastructure-as-code, and monitoring tools are all CLI‑first.
argparse and sys.argv are the industry standard
for building reliable automation scripts.
The curriculum you're about to explore is not academic — it's the practical foundation used by thousands of open‑source projects and Fortune 500 companies. Every concept maps directly to real‑world problems you'll face as a professional developer.
📑 Table of Contents
Jump to any section — all entries are equal size
⌨️ The input() Function — Your Gateway to User Interaction
Every interactive Python program starts here. input() reads a line from the user and returns it as a string.
📝 1.1 Basic input() — Prompt and Capture
The simplest form: call input() with an optional prompt string. The program pauses until the user types something and presses Enter. The returned value is always a string.
Step-by-step explanation:
- Step 1: Define the function
basic_input_demo()— this groups our code for reuse. - Step 2: Call
input("What is your name? ")— this displays the prompt and waits for the user to type something and press Enter. - Step 3: The value typed by the user is stored in the variable
nameas a string. - Step 4: Use an f-string with
print()to display a personalized greeting:f"Hello, {name}!". - Step 5: Call
input("How old are you? ")to capture the user's age — again stored as a string. - Step 6: Display the age back to the user using an f-string.
- Step 7: Use
type(age)to show thatinput()always returns a string — even for numbers. - Step 8: Call the function
basic_input_demo()to execute the code.
def basic_input_demo(): name = input("What is your name? ") print(f"Hello, {name}! Nice to meet you.") age = input("How old are you? ") print(f"You are {age} years old.") print(f"Type of age: {type(age)}") basic_input_demo()
input() always returns a string. Even if the user types "42", you get "42" — not the integer 42. Conversion is your responsibility.📝 1.2 Multi-line Prompts
Use triple-quoted strings or \n for multi-line prompts. This is useful for complex instructions or menus.
Step-by-step explanation:
- Step 1: Define a multi-line string using triple quotes
""" ... """— this preserves line breaks. - Step 2: Print the multi-line prompt to display a formatted menu or welcome message.
- Step 3: Call
input()three times to capture two numbers and an operation from the user. - Step 4: Display the captured inputs back to the user using an f-string.
- Step 5: Call the function to run the interactive prompt.
def multi_line_prompt(): prompt = """ ─── Welcome to the Calculator ─── Enter two numbers and an operation. """ print(prompt) a = input("First number: ") b = input("Second number: ") op = input("Operation (+, -, *, /): ") print(f"Inputs: {a} {op} {b}") multi_line_prompt()
📝 1.3 Empty Input — Handling ''
If the user just presses Enter without typing anything, input() returns an empty string ''. Always plan for this — many bugs come from assuming input is non-empty.
Step-by-step explanation:
- Step 1: Call
input()with a prompt asking the user to type something. - Step 2: Store the result in
response— it could be an empty string if the user pressed Enter. - Step 3: Check if
response == ""to detect empty input and display a specific message. - Step 4: If the input is not empty, display what the user typed.
- Step 5: Use
len(response)to show the character count — 0 for empty input. - Step 6: Call the function to test the behavior.
def handle_empty(): response = input("Type something (or press Enter): ") if response == "": print("You pressed Enter without typing anything!") else: print(f"You typed: {response}") print(f"Length: {len(response)} characters") handle_empty()
if not response: to check for empty input — it's cleaner than == "".🔄 Type Conversion — Turning Strings into Numbers
input() always returns a string. To do math, you must convert to int, float, or bool.
🔢 2.1 int() — Convert to Integer
Use int() to convert a string to an integer. If the string contains non-numeric characters, Python raises a ValueError.
Step-by-step explanation:
- Step 1: Call
input()to capture a string from the user (age). - Step 2: Pass the captured string to
int()to convert it to an integer. - Step 3: Store the converted integer in the variable
age. - Step 4: Perform arithmetic —
age + 1calculates next year's age. - Step 5: Use
type(age)to confirm the variable is now anint. - Step 6: Demonstrate
int("1010", base=2)to convert a binary string to decimal. - Step 7: Call the function to run the code.
def int_conversion_demo(): age_str = input("Enter your age: ") age = int(age_str) print(f"Next year you will be {age + 1}") print(f"Type of age: {type(age)}") binary = "1010" decimal = int(binary, base=2) print(f"Binary {binary} = decimal {decimal}") int_conversion_demo()
int("3.14") raises ValueError — use float() for decimal numbers, then convert to int if needed.🔢 2.2 float() — Convert to Floating-Point
Use float() for numbers with decimals. Accepts strings like "3.14", "-2.5", and "1e-3".
Step-by-step explanation:
- Step 1: Call
input()with a prompt asking for a price (e.g., "19.99"). - Step 2: Use
float()to convert the string to a floating-point number. - Step 3: Calculate tax as 8% of the price using multiplication:
price * 0.08. - Step 4: Add the tax to the price to get the total.
- Step 5: Use f-string formatting with
:.2fto display currency with 2 decimal places. - Step 6: Use
type(price)to confirm the variable is afloat. - Step 7: Call the function to execute the code.
def float_conversion_demo(): price_str = input("Enter the price: $") price = float(price_str) tax = price * 0.08 total = price + tax print(f"Price: ${price:.2f}") print(f"Tax (8%): ${tax:.2f}") print(f"Total: ${total:.2f}") print(f"Type of price: {type(price)}") float_conversion_demo()
🔢 2.3 bool() — Convert to Boolean
bool() converts any value to True or False. For strings, only "" is False — all non-empty strings are True.
Step-by-step explanation:
- Step 1: Call
input()to capture a string from the user. - Step 2: Pass the string to
bool()to convert it to a boolean. - Step 3: Display the boolean result using an f-string.
- Step 4: Use an
ifstatement to check the boolean value and display a message. - Step 5: Show examples of
bool(0),bool(42), andbool('yes'). - Step 6: Call the function to run the code.
def bool_conversion_demo(): user_input = input("Enter something: ") value = bool(user_input) print(f"bool('{user_input}') = {value}") if value: print("You entered something non-empty!") else: print("You entered nothing.") print(f"bool(0) = {bool(0)}") print(f"bool(42) = {bool(42)}") print(f"bool('yes') = {bool('yes')}") bool_conversion_demo()
if response.lower() in ("yes", "y"): rather than relying on bool().✅ Input Validation — Trust Nothing, Verify Everything
Users make mistakes. Malicious users exploit input. Validate early and often.
🛡️ 3.1 Basic Validation with try/except
Wrap conversion in a try/except block to catch invalid input gracefully.
Step-by-step explanation:
- Step 1: Define a function
validate_integer()to handle validation. - Step 2: Inside a
tryblock, callinput()and convert withint(). - Step 3: If conversion succeeds, print a success message and
returnthe value. - Step 4: In the
except ValueErrorblock, print an error message andreturn None. - Step 5: Call the function and check if the result is
not Nonebefore using it. - Step 6: If valid, double the number and display it.
def validate_integer(): try: value = int(input("Enter a whole number: ")) print(f"✅ You entered: {value}") return value except ValueError: print("❌ That's not a valid whole number!") return None result = validate_integer() if result is not None: print(f"Double it: {result * 2}")
🛡️ 3.2 Loop Until Valid — Robust User Experience
Keep asking until the user provides valid input. This pattern is essential for interactive CLI tools.
Step-by-step explanation:
- Step 1: Define
get_positive_number(prompt)that loops until valid input is received. - Step 2: Inside an infinite
while Trueloop, attempt to convert the input tofloat. - Step 3: If conversion succeeds and the value is positive,
returnthe value (exits the function). - Step 4: If the value is not positive, show an error message and continue the loop.
- Step 5: If conversion fails with
ValueError, show an error and continue. - Step 6: In
main(), call the function with a prompt and calculate the area of a circle. - Step 7: Display the calculated area with 2 decimal places.
def get_positive_number(prompt: str) -> float: while True: try: value = float(input(prompt)) if value > 0: return value print("❌ Please enter a positive number.") except ValueError: print("❌ Invalid input. Please enter a number.") def main(): radius = get_positive_number("Enter radius: ") area = 3.14159 * radius ** 2 print(f"✅ Area of circle: {area:.2f}") main()
while True + return pattern is the cleanest way to loop until valid input is received. No messy flag variables.🛡️ 3.3 Range Validation — Check Bounds
Sometimes you need a number within a specific range (e.g., 1–100, 0–10). Add bounds checking after conversion.
Step-by-step explanation:
- Step 1: Define
get_score()that validates a score between 0 and 100. - Step 2: Inside the loop, attempt to convert input to
int. - Step 3: Check if the score is between 0 and 100 inclusive — if so,
returnit. - Step 4: If out of range, show a message and continue the loop.
- Step 5: Define
grade_score()that returns a letter grade based on the score. - Step 6: Call
get_score()to get a valid score, then pass it tograde_score(). - Step 7: Display the score and its corresponding grade.
def get_score() -> int: while True: try: score = int(input("Enter a score (0–100): ")) if 0 <= score <= 100: return score print("❌ Score must be between 0 and 100.") except ValueError: print("❌ Please enter a whole number.") def grade_score(score: int) -> str: if score >= 90: return "A" if score >= 80: return "B" if score >= 70: return "C" if score >= 60: return "D" return "F" score = get_score() print(f"✅ Score {score} → Grade {grade_score(score)}")
🛡️ 3.4 String Validation — Check Format and Content
Validate strings for length, allowed characters, or pattern matching with regex.
Step-by-step explanation:
- Step 1: Import the
remodule for regular expression matching. - Step 2: Define
validate_name()that loops until a valid name is entered. - Step 3: Use
len()to check that the name is between 2 and 30 characters. - Step 4: Use
re.match()with a pattern to ensure only letters, spaces, hyphens, and apostrophes. - Step 5: Define
validate_email()that loops until a valid email is entered. - Step 6: Use a regex pattern to check the email format (basic validation).
- Step 7: Call both validation functions and display a welcome message.
import re def validate_name() -> str: while True: name = input("Enter your name (2–30 letters): ").strip() if len(name) < 2 or len(name) > 30: print("❌ Name must be 2–30 characters.") continue if not re.match(r"^[A-Za-z\s\-'']+$", name): print("❌ Name contains invalid characters.") continue return name def validate_email() -> str: while True: email = input("Enter your email: ").strip() if re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email): return email print("❌ Invalid email format.") name = validate_name() email = validate_email() print(f"✅ Welcome, {name}! Email: {email}")
str.strip() to remove accidental leading/trailing spaces before validation.📦 Multiple Inputs — Collecting Several Values at Once
Sometimes you need to collect multiple values in a single line, or ask for a list of items.
📦 4.1 split() — Parse Space-Separated Input
Use str.split() to break a single input line into multiple pieces. Then convert each piece individually.
Step-by-step explanation:
- Step 1: Call
input()to capture a line with three numbers separated by spaces. - Step 2: Use
split()to break the string into a list of parts. - Step 3: Check that exactly three parts were entered — if not, show an error and return.
- Step 4: Convert each part to
floatinside atry/exceptblock. - Step 5: Calculate the sum and average of the three numbers.
- Step 6: Display the results with 2 decimal places.
def parse_multiple_numbers(): raw = input("Enter three numbers separated by spaces: ") parts = raw.split() print(f"Parts: {parts}") if len(parts) != 3: print("❌ Please enter exactly three numbers.") return try: a, b, c = float(parts[0]), float(parts[1]), float(parts[2]) print(f"✅ Sum: {a + b + c:.2f}") print(f"✅ Average: {(a + b + c) / 3:.2f}") except ValueError: print("❌ All inputs must be numbers.") parse_multiple_numbers()
📦 4.2 Collecting an Arbitrary Number of Items
Use a loop to collect items until the user signals they are done (e.g., with a blank line or a sentinel value).
Step-by-step explanation:
- Step 1: Define
collect_items()that returns a list of strings. - Step 2: Print instructions for the user: enter items one per line, press Enter twice to finish.
- Step 3: Inside an infinite loop, call
input()to get each line. - Step 4: If the line is empty (
""), break out of the loop. - Step 5: Otherwise, append the line to the
itemslist. - Step 6: In
main(), display the collected items with numbers. - Step 7: Call the function to run the interactive collection.
def collect_items() -> list: items = [] print("Enter items one per line. Press Enter twice to finish.") while True: line = input("> ") if line == "": break items.append(line) return items def main(): items = collect_items() print(f"\nYou entered {len(items)} items:") for i, item in enumerate(items, 1): print(f" {i}. {item}") main()
💻 Command-Line Arguments — sys.argv
Input isn't just interactive — many Python scripts are called with arguments from the shell.
📂 5.1 sys.argv — The Argument List
sys.argv is a list of all command-line arguments passed to the script. The first element sys.argv[0] is the script name itself.
Step-by-step explanation:
- Step 1: Import the
sysmodule to access command-line arguments. - Step 2: Define
show_argv()to display all arguments. - Step 3:
sys.argv[0]is always the script name (preset in this environment). - Step 4:
len(sys.argv) - 1gives the number of arguments passed. - Step 5: Use
enumerate(sys.argv[1:], 1)to loop through each argument. - Step 6: If there is at least one argument, display the first one.
- Step 7: Call the function to show the arguments.
import sys def show_argv(): print(f"Script name: {sys.argv[0]}") print(f"Number of arguments: {len(sys.argv) - 1}") for i, arg in enumerate(sys.argv[1:], 1): print(f" Arg {i}: {arg}") if len(sys.argv) > 1: print(f"\nFirst argument: {sys.argv[1]}") show_argv()
sys.argv is preset to ['script.py', 'Alice', '28'] so the demo has something to show. In a real terminal, it reflects the arguments you typed.📂 5.2 Simple Argument Parser — Manual Parsing
For simple scripts, manually parse sys.argv. Check the number of arguments and handle each position.
Step-by-step explanation:
- Step 1: Import
sysfor command-line access. - Step 2: Check if fewer than 2 arguments were provided — show usage message if so.
- Step 3:
sys.argv[1]is the first argument (the name). - Step 4: Check if a second argument (age) was provided — if so, try to convert to
int. - Step 5: If conversion fails, set age to "invalid".
- Step 6: Display a greeting with the name and age.
- Step 7: Call the function to demonstrate.
import sys def simple_cli(): if len(sys.argv) < 2: print("Usage: python script.py <name> [age]") return name = sys.argv[1] age = "unknown" if len(sys.argv) >= 3: try: age = int(sys.argv[2]) except ValueError: age = "invalid" print(f"Hello, {name}!") print(f"Age: {age}") simple_cli()
⚙️ argparse — Professional Command-Line Parsing
For robust, production-ready CLI tools, argparse is the standard library module that handles flags, options, subcommands, and help text automatically.
⚙️ 6.1 Basic argparse Setup
Define arguments, flags, and options with type checking and automatic help generation.
Step-by-step explanation:
- Step 1: Import the
argparsemodule. - Step 2: Create an
ArgumentParserinstance with a description. - Step 3: Add a positional argument:
"name"— this is required. - Step 4: Add an optional argument:
"--age"with typeintand default 18. - Step 5: Add a flag:
"--verbose"withaction="store_true". - Step 6: Parse arguments with
parser.parse_args(), passing an explicit list of fake CLI arguments rather than reading real argv. - Step 7: Access values via
args.name,args.age,args.verbose. - Step 8: Display the parsed values and check if the user is an adult.
import argparse def build_parser(): parser = argparse.ArgumentParser( description="A simple CLI tool with argparse" ) parser.add_argument("name", help="Your name") parser.add_argument("--age", type=int, default=18, help="Your age (default: 18)") parser.add_argument("--verbose", action="store_true", help="Enable verbose output") return parser def main(): parser = build_parser() args = parser.parse_args(["Alice", "--age", "25", "--verbose"]) if args.verbose: print("🔍 Verbose mode enabled") print(f"Hello, {args.name}!") print(f"Age: {args.age}") if args.age >= 18: print("✅ You are an adult.") else: print("🔞 You are a minor.") main()
argparse automatically generates --help output, validates types, and provides error messages for invalid input. Note we pass an explicit list to parse_args() instead of reading real sys.argv — this keeps the demo self-contained and avoids SystemExit surprises.⚙️ 6.2 Positional vs Optional Arguments
Positional arguments are required and ordered. Optional arguments are prefixed with -- and can appear anywhere.
Step-by-step explanation:
- Step 1: Create a parser with a description for file operations.
- Step 2: Add
"source"as a required positional argument. - Step 3: Add
"dest"as an optional positional withnargs="?"and a default value. - Step 4: Add
"-v"/"--verbose"as a flag (store_true). - Step 5: Add
"-f"/"--force"as a flag (store_true). - Step 6: Parse arguments with an explicit fake-argument list.
- Step 7: Display all parsed values to show how each argument is handled.
import argparse def create_parser(): parser = argparse.ArgumentParser(description="File operations") parser.add_argument("source", help="Source file path") parser.add_argument("dest", nargs="?", default=".", help="Destination path (default: current dir)") parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output") parser.add_argument("-f", "--force", action="store_true", help="Overwrite without asking") return parser def main(): parser = create_parser() args = parser.parse_args(["report.txt", "/backup/", "--verbose"]) print(f"Source: {args.source}") print(f"Destination: {args.dest}") print(f"Verbose: {args.verbose}") print(f"Force: {args.force}") main()
🔒 Secure Input — getpass for Passwords
Never echo passwords to the screen. Use getpass for secure, hidden input.
🔒 7.1 getpass.getpass() — Hidden Input
This function works like input() but doesn't echo characters to the console. Perfect for passwords, API keys, and sensitive data.
Step-by-step explanation:
- Step 1: Import the
getpassmodule. - Step 2: Use
input()for the username (visible input). - Step 3: Use
getpass.getpass()for the password (hidden input — in this browser sandbox it's mocked with a JS prompt dialog). - Step 4: Display the username and the length of the password (not the password itself).
- Step 5: Check if the credentials match a hardcoded pair and display success or failure.
- Step 6: Call the function to test secure login.
import getpass def secure_login(): username = input("Username: ") password = getpass.getpass("Password: ") print(f"\nUsername: {username}") print(f"Password length: {len(password)} characters (hidden)") if username == "admin" and password == "secret123": print("✅ Login successful!") else: print("❌ Invalid credentials.") secure_login()
getpass is mocked using a JavaScript prompt() dialog (it still doesn't echo into the page output). In a local terminal, the real getpass hides typed characters entirely.🔒 7.2 Password Confirmation Pattern
When creating new passwords, ask twice and compare. This prevents typos and ensures accuracy.
Step-by-step explanation:
- Step 1: Import
getpassfor hidden input. - Step 2: Ask for the current password using
getpass(). - Step 3: Check if the current password matches the expected value.
- Step 4: Ask for the new password twice using
getpass(). - Step 5: Compare the two new passwords — if they don't match, show an error.
- Step 6: Check if the new password is at least 8 characters long.
- Step 7: If all checks pass, confirm the password change.
import getpass def change_password(): print("🔐 Change Password") print("-" * 30) current = getpass.getpass("Current password: ") if current != "secret123": print("❌ Incorrect current password.") return new1 = getpass.getpass("New password: ") new2 = getpass.getpass("Confirm new password: ") if new1 != new2: print("❌ Passwords do not match.") return if len(new1) < 8: print("❌ Password must be at least 8 characters.") return print("✅ Password changed successfully!") change_password()
📂 Reading Input from Files
Sometimes input comes from a file, not from a user typing. Learn to read structured data from CSV, JSON, and text files.
📄 8.1 Reading Text Files
Use open() with a context manager to read files safely. Always specify encoding.
Step-by-step explanation:
- Step 1: Define
read_text_file(path)that attempts to read a file. - Step 2: Use
open()with"r"mode andencoding="utf-8"inside awithblock. - Step 3: Read the entire file content with
f.read(). - Step 4: Display the file path, character count, and first 200 characters.
- Step 5: Catch
FileNotFoundErrorand display a friendly error. - Step 6: Catch any other exceptions and display the error.
- Step 7: Call the function with a sample file path that this environment pre-creates for you.
def read_text_file(path: str): try: with open(path, "r", encoding="utf-8") as f: content = f.read() print(f"📄 File: {path}") print(f"Characters: {len(content)}") print("Content:") print(content[:200]) except FileNotFoundError: print(f"❌ File not found: {path}") except Exception as e: print(f"❌ Error reading file: {e}") read_text_file("sample.txt")
sample.txt in the virtual filesystem before your code runs, so this demo works out of the box. In a local environment, it reads from the actual filesystem.📄 8.2 Reading CSV Files
Use the csv module to read structured tabular data with proper quoting and escaping.
Step-by-step explanation:
- Step 1: Import the
csvmodule andStringIOfor in-memory parsing. - Step 2: Define
parse_csv_data(csv_text)that takes a CSV string. - Step 3: Create a
DictReaderfromStringIO(csv_text). - Step 4: Convert the reader to a list of dictionaries (rows).
- Step 5: Display the number of rows and the column names.
- Step 6: Loop through the first 3 rows and print each row.
- Step 7: Define a sample CSV string and call the function.
import csv from io import StringIO def parse_csv_data(csv_text: str): reader = csv.DictReader(StringIO(csv_text)) rows = list(reader) print(f"📊 Found {len(rows)} rows") if rows: print(f"Columns: {', '.join(rows[0].keys())}") for row in rows[:3]: print(row) csv_sample = """name,age,city Alice,30,New York Bob,25,London Charlie,35,Paris""" parse_csv_data(csv_sample)
📄 8.3 Reading JSON Files
JSON is the most common format for APIs and configuration. Use json.load() or json.loads().
Step-by-step explanation:
- Step 1: Import the
jsonmodule. - Step 2: Define a sample JSON string (not a dict) with user data.
- Step 3: Define
parse_json(data)that parses a JSON string. - Step 4: Use
json.loads()to convert the string to a Python dictionary. - Step 5: Access values using dictionary keys:
obj["user"],obj["level"], etc. - Step 6: Calculate the average of the scores list.
- Step 7: Catch
JSONDecodeErrorfor invalid JSON.
import json json_sample = '{"user": "glenn", "level": "advanced", "scores": [95, 88, 92]}' def parse_json(data: str): try: obj = json.loads(data) print("✅ JSON parsed successfully") print(f"User: {obj['user']}") print(f"Level: {obj['level']}") print(f"Scores: {obj['scores']}") print(f"Average: {sum(obj['scores']) / len(obj['scores']):.1f}") except json.JSONDecodeError as e: print(f"❌ Invalid JSON: {e}") parse_json(json_sample)
json.loads() requires a string argument.🔄 Input in Loops — Interactive Programs
Many programs need to keep asking for input until a condition is met. Master the interactive loop.
🔄 9.1 Sentinel Loop — Quit with Special Value
Use a sentinel value (like "quit" or 0) to let the user exit the loop.
Step-by-step explanation:
- Step 1: Define
sentinel_loop()withtotalandcountvariables. - Step 2: Print instructions: type "quit" to stop.
- Step 3: Inside
while True, callinput()and store inraw. - Step 4: Check if
raw.lower() == "quit"— if so, break the loop. - Step 5: Try to convert
rawtofloat— if successful, add to total and increment count. - Step 6: If conversion fails, show an error and continue.
- Step 7: After the loop, display the summary if any numbers were entered.
def sentinel_loop(): total = 0 count = 0 print("Enter numbers to sum. Type 'quit' to stop.") while True: raw = input("> ") if raw.lower() == "quit": break try: num = float(raw) total += num count += 1 print(f"✅ Added {num}. Running total: {total:.2f}") except ValueError: print("❌ Invalid number. Try again.") if count > 0: print(f"📊 Summary: {count} numbers, total {total:.2f}, average {total/count:.2f}") else: print("No numbers entered.") sentinel_loop()
🔄 9.2 Interactive Menu — User-Driven Program
A menu-driven program lets the user choose actions repeatedly until they decide to exit.
Step-by-step explanation:
- Step 1: Define
show_menu()that prints the menu options. - Step 2: Define
menu_loop()with an infinite loop. - Step 3: Call
show_menu()and capture the user's choice. - Step 4: If choice is "0", print goodbye and break.
- Step 5: If choice is "1", ask for a name and greet the user.
- Step 6: If choice is "2", ask for a number and double it.
- Step 7: If choice is "3", import
timeand show the current time. - Step 8: For any other choice, show an error message.
def show_menu(): print("\n📋 Menu") print(" 1. Say hello") print(" 2. Double a number") print(" 3. Show current time") print(" 0. Exit") def menu_loop(): while True: show_menu() choice = input("Choose an option: ").strip() if choice == "0": print("👋 Goodbye!") break elif choice == "1": name = input("Your name: ") print(f"Hello, {name}! 👋") elif choice == "2": try: num = float(input("Enter a number: ")) print(f"Double: {num * 2}") except ValueError: print("❌ Invalid number.") elif choice == "3": import time print(f"🕐 {time.strftime('%H:%M:%S')}") else: print("❌ Invalid option. Try again.") menu_loop()
🛡️ Robust Error Handling for User Input
Anticipate every possible failure and handle it gracefully — your users will thank you.
🛡️ 10.1 Comprehensive Validation Function
Build a reusable validation function that handles multiple failure modes and provides clear feedback.
Step-by-step explanation:
- Step 1: Import
Optionalfromtypingfor type hints. - Step 2: Define
safe_int_input()with parameters for prompt, min, max, and retries. - Step 3: Loop for the specified number of retries.
- Step 4: Inside each attempt, try to convert the input to
int. - Step 5: Check if the value is within the specified min and max bounds.
- Step 6: If valid, return the value immediately.
- Step 7: If invalid or conversion fails, show an error and continue.
- Step 8: After all retries, return
None. - Step 9: Test the function with age and score inputs.
from typing import Optional def safe_int_input( prompt: str, min_val: Optional[int] = None, max_val: Optional[int] = None, retries: int = 3 ) -> Optional[int]: for attempt in range(1, retries + 1): try: raw = input(f"{prompt} ") value = int(raw) if min_val is not None and value < min_val: print(f"❌ Value must be at least {min_val}.") continue if max_val is not None and value > max_val: print(f"❌ Value must be at most {max_val}.") continue return value except ValueError: print(f"❌ Invalid integer. ({retries - attempt} attempts left)") print("❌ Too many attempts. Returning None.") return None age = safe_int_input("Enter your age:", min_val=1, max_val=120) if age is not None: print(f"✅ Age: {age}") score = safe_int_input("Enter a score (0-100):", min_val=0, max_val=100) if score is not None: print(f"✅ Score: {score}")
🛡️ 10.2 Context Managers for Resource Safety
When reading files, use with to guarantee the file is closed even if an exception occurs.
Step-by-step explanation:
- Step 1: Define
read_file_safely(path)that attempts to read a file. - Step 2: Use
with open()to open the file — this automatically closes it. - Step 3: Read the content and return it.
- Step 4: Catch
FileNotFoundErrorand display a specific error. - Step 5: Catch
PermissionErrorfor access issues. - Step 6: Catch
UnicodeDecodeErrorfor encoding problems. - Step 7: Catch any other unexpected exceptions with a generic handler.
- Step 8: Call the function and check if content was read successfully.
def read_file_safely(path: str): try: with open(path, "r", encoding="utf-8") as f: return f.read() except FileNotFoundError: print(f"❌ File not found: {path}") return None except PermissionError: print(f"❌ Permission denied: {path}") return None except UnicodeDecodeError as e: print(f"❌ Encoding error: {e}") return None except Exception as e: print(f"❌ Unexpected error: {e}") return None content = read_file_safely("sample.txt") if content is not None: print(f"✅ Read {len(content)} characters")
🚀 Real-World Input Patterns
Putting it all together — input handling patterns used in production Python applications.
🚀 11.1 Configuration Input — Interactive Setup
Many applications ask for configuration on first run. This pattern collects settings and saves them.
Step-by-step explanation:
- Step 1: Import
jsonandPathfor file operations. - Step 2: Define
interactive_config()that returns a dictionary of settings. - Step 3: Ask for project name with a default value using the
oroperator. - Step 4: Ask for port number — convert to int or use default 8000.
- Step 5: Ask for debug mode — check if input is "yes", "y", or "true".
- Step 6: Ask for environment — default to "dev".
- Step 7: Define
save_config()that writes the config to a JSON file. - Step 8: Call both functions and display the configuration.
import json from pathlib import Path def interactive_config() -> dict: print("\n⚙️ Interactive Configuration Setup") print("Press Enter to use default values.") config = {} name = input("Project name [default: myproject]: ") or "myproject" config["name"] = name port_input = input("Port [default: 8000]: ") config["port"] = int(port_input) if port_input else 8000 debug_input = input("Debug mode? (yes/no) [default: no]: ") config["debug"] = debug_input.lower() in ("yes", "y", "true") env = input("Environment [dev/prod, default: dev]: ") or "dev" config["environment"] = env return config def save_config(config: dict, path: str = "config.json"): with open(path, "w", encoding="utf-8") as f: json.dump(config, f, indent=2) print(f"✅ Config saved to {path}") config = interactive_config() print("\n📋 Configuration:") for k, v in config.items(): print(f" {k}: {v}")
🚀 11.2 Data Entry Form — Multi-Field Collection
Collect structured data from the user with validation and confirmation.
Step-by-step explanation:
- Step 1: Import
refor regular expression validation. - Step 2: Define
collect_person_data()that returns a dictionary. - Step 3: Validate name: check length and character set using
re.match(). - Step 4: Validate email: check format with a regex pattern.
- Step 5: Validate age: convert to
intand check range 1–150. - Step 6: Ask for role with a predefined set of options — default to "other".
- Step 7: Return the collected data as a dictionary.
- Step 8: Display the collected person data.
import re def collect_person_data() -> dict: print("\n📝 Enter person details:") while True: name = input("Full name: ").strip() if len(name) >= 2 and re.match(r"^[A-Za-z\s\-'']+$", name): break print("❌ Please enter a valid name (2+ letters, no numbers).") while True: email = input("Email: ").strip() if re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email): break print("❌ Invalid email format.") while True: age_str = input("Age: ") try: age = int(age_str) if 1 <= age <= 150: break print("❌ Age must be between 1 and 150.") except ValueError: print("❌ Please enter a valid number.") role = input("Role (developer, designer, manager): ").strip().lower() if role not in ("developer", "designer", "manager"): role = "other" print(f"Role set to {role}") return {"name": name, "email": email, "age": age, "role": role} person = collect_person_data() print("\n✅ Person saved:") for k, v in person.items(): print(f" {k}: {v}")
🚀 11.3 Command-Line Tool with Multiple Modes
A real CLI tool with subcommands, each with its own arguments.
Step-by-step explanation:
- Step 1: Import
argparsefor subcommand support. - Step 2: Define
build_subcommand_parser()that creates a parser with subparsers. - Step 3: Add an "add" subcommand with a required
titleargument and optional--priority. - Step 4: Add a "list" subcommand with an optional
--filterargument. - Step 5: Add a "done" subcommand with a required
task_idargument. - Step 6: In
main(), parse arguments and check thecommandattribute. - Step 7: Handle each subcommand with appropriate logic.
- Step 8: Call the function to demonstrate subcommand parsing.
import argparse def build_subcommand_parser(): parser = argparse.ArgumentParser(description="Task manager CLI") subparsers = parser.add_subparsers(dest="command", required=True) add_parser = subparsers.add_parser("add", help="Add a task") add_parser.add_argument("title", help="Task title") add_parser.add_argument("--priority", choices=["low", "medium", "high"], default="medium") list_parser = subparsers.add_parser("list", help="List tasks") list_parser.add_argument("--filter", choices=["all", "active", "done"], default="all") done_parser = subparsers.add_parser("done", help="Mark task as done") done_parser.add_argument("task_id", type=int, help="Task ID") return parser def main(): parser = build_subcommand_parser() args = parser.parse_args(["add", "Buy milk", "--priority", "high"]) if args.command == "add": print(f"✅ Added task: '{args.title}' (priority: {args.priority})") elif args.command == "list": print(f"📋 Listing tasks (filter: {args.filter})") elif args.command == "done": print(f"✅ Task {args.task_id} marked as done") main()
git, docker, pip — uses this pattern.❓ Frequently Asked Questions — Input Handling
Common questions about Python input, answered.
Why does input() always return a string?
+Python is strongly typed and does not auto-convert. This design makes the program's behaviour predictable and explicit. You — the programmer — decide what type the input should be and convert it accordingly.
How do I get a number without ValueError?
+Wrap int() or float() in a try/except block. For user-facing programs, loop until valid input is received. Never assume input will be correct.
What is the difference between sys.argv and argparse?
+sys.argv is a raw list of arguments. argparse is a full-featured parser that handles flags, options, type conversion, help generation, and error messages. Use argparse for any non-trivial CLI tool.
How do I read input that has spaces?
+input() reads the entire line, including spaces. For multiple values on one line, use split() and then process each piece. For multi-line input, collect in a loop until a sentinel value is entered.
Is getpass safe to use in production?
+getpass is safe for hiding input from shoulder surfers, but it does not encrypt the input. Use it for passwords in local scripts. For production applications, use environment variables or dedicated secret management tools.
How do I handle Unicode input?
+Python 3 handles Unicode strings by default. input() returns a str that can contain any Unicode character. When reading from files, always specify encoding="utf-8".
What's the best way to validate email addresses?
+Use a regular expression for basic format validation. For production, consider using a dedicated library like email-validator. Never try to fully validate an email address with regex alone — send a confirmation link to verify.
💛 You've Mastered Python Input Handling
From the humble input() to professional CLI tools with argparse,
you now have a complete mental model for handling user input in Python. You've learned to
validate, convert, secure, and
structure input in ways that scale from simple scripts to production applications.
This curriculum, crafted by @PoeticGlenn, is designed to be your go-to reference whenever you need to handle input in Python. Bookmark it, share it with your team, and revisit the lessons as you build more complex systems.
"Great input handling is invisible — it just works. That's the mark of a professional developer." — Glenn Junsay Pansensoy (@PoeticGlenn)