Python – User Input

Every meaningful program needs a way to communicate with its users. User input is the bridge between your program and the person running it — it transforms a static script into an interactive experience. Whether you are building a command-line tool, a data processing pipeline, or a quick utility script, understanding how to collect, validate, and safely handle user input is a foundational skill that separates production-ready code from toy examples.

In this tutorial, we will explore every major technique Python offers for gathering user input: from the built-in input() function all the way to command-line argument parsing, stdin piping, and secure password prompts. Along the way, we will build real programs and discuss the security pitfalls that trip up even experienced developers.

The input() Function — Basics

The input() function is Python’s primary tool for reading interactive input from the console. When called, it displays an optional prompt string, pauses execution, and waits for the user to type something and press Enter. The value is always returned as a string, regardless of what the user types.

Syntax

variable = input(prompt_message)

The prompt_message is optional but strongly recommended. A clear prompt tells users exactly what you expect from them, which reduces errors and frustration.

Example

name = input("What is your name: ")
print(f"Hello, {name}! Welcome to the tutorial.")

Output

What is your name: Folau
Hello, Folau! Welcome to the tutorial.

Notice we use an f-string for formatting. This is the modern, preferred approach in Python 3.6+ because it is more readable and less error-prone than string concatenation or .format().

Type Conversion of Input

Since input() always returns a string, you must explicitly convert the value when you need a number, a float, or another type. This is one of the most common sources of bugs for beginners.

Converting to int

age = int(input("Enter your age: "))
print(f"In 10 years, you will be {age + 10} years old.")

Converting to float

price = float(input("Enter the item price: $"))
tax = price * 0.08
total = price + tax
print(f"Price: ${price:.2f}, Tax: ${tax:.2f}, Total: ${total:.2f}")

Converting to bool

There is no direct bool(input()) pattern that works intuitively, because bool("False") returns True (any non-empty string is truthy). Instead, compare against expected values explicitly:

response = input("Continue? (yes/no): ").strip().lower()
should_continue = response in ("yes", "y")
print(f"Continuing: {should_continue}")

Input Validation

Never trust user input. Users will type unexpected things — empty strings, letters when you expect numbers, negative values when you need positive ones. Validating input before using it is a hallmark of professional code.

Checking for empty input

username = input("Enter your username: ").strip()

if not username:
    print("Error: Username cannot be empty.")
else:
    print(f"Welcome, {username}!")

Checking numeric ranges

age = int(input("Enter your age: "))

if age < 0 or age > 150:
    print("Error: Please enter a realistic age.")
else:
    print(f"Your age is {age}.")

Checking string patterns

email = input("Enter your email: ").strip()

if "@" not in email or "." not in email:
    print("Error: That does not look like a valid email address.")
else:
    print(f"Email saved: {email}")

Handling Invalid Input with try/except

When converting user input to a number, the program will crash with a ValueError if the user types something that cannot be converted. The try/except block lets you handle this gracefully instead of letting your program blow up.

try:
    number = int(input("Enter a whole number: "))
    print(f"You entered: {number}")
except ValueError:
    print("Error: That is not a valid integer.")

Input validation loop — keep asking until valid

In practice, you almost always want to combine try/except with a loop so the user gets another chance:

def get_positive_integer(prompt):
    """Keep asking until the user provides a valid positive integer."""
    while True:
        try:
            value = int(input(prompt))
            if value <= 0:
                print("Error: Please enter a positive number.")
                continue
            return value
        except ValueError:
            print("Error: Invalid input. Please enter a whole number.")

# Usage
quantity = get_positive_integer("How many items? ")
print(f"You ordered {quantity} items.")

This pattern is extremely common in production code. Extract it into a reusable function so you do not repeat yourself.

Getting Multiple Inputs with split()

Sometimes you need multiple values in a single line. The split() method breaks a string into a list of substrings, which you can then unpack or process.

Space-separated values

first, last = input("Enter your first and last name: ").split()
print(f"First: {first}, Last: {last}")

Comma-separated values

values = input("Enter numbers separated by commas: ").split(",")
numbers = [int(v.strip()) for v in values]
print(f"Sum: {sum(numbers)}, Average: {sum(numbers) / len(numbers):.2f}")

Using map() for cleaner conversion

x, y, z = map(int, input("Enter three integers (space-separated): ").split())
print(f"x={x}, y={y}, z={z}, sum={x + y + z}")

Command-Line Arguments

For scripts that run in automated pipelines or are invoked from the terminal, command-line arguments are often a better choice than interactive prompts. Python provides two main approaches.

sys.argv — The Simple Approach

sys.argv is a list of strings passed to your script from the command line. The first element (sys.argv[0]) is the script name itself.

import sys

if len(sys.argv) != 3:
    print("Usage: python greet.py <first_name> <last_name>")
    sys.exit(1)

first_name = sys.argv[1]
last_name = sys.argv[2]
print(f"Hello, {first_name} {last_name}!")

Run it from the terminal:

# Terminal
$ python greet.py Folau Kaveinga
Hello, Folau Kaveinga!

argparse — The Professional Approach

For anything beyond trivial scripts, argparse is the standard library module for building proper command-line interfaces. It handles argument parsing, type conversion, help text, and error messages automatically.

import argparse

parser = argparse.ArgumentParser(description="A simple greeting tool.")
parser.add_argument("name", help="The name to greet")
parser.add_argument("-c", "--count", type=int, default=1,
                    help="Number of times to greet (default: 1)")
parser.add_argument("-u", "--uppercase", action="store_true",
                    help="Print greeting in uppercase")

args = parser.parse_args()

greeting = f"Hello, {args.name}!"
if args.uppercase:
    greeting = greeting.upper()

for _ in range(args.count):
    print(greeting)

Run it from the terminal:

# Terminal
$ python greet.py Folau --count 3 --uppercase
HELLO, FOLAU!
HELLO, FOLAU!
HELLO, FOLAU!

$ python greet.py --help
usage: greet.py [-h] [-c COUNT] [-u] name
...

argparse automatically generates --help output and provides clear error messages when users pass invalid arguments. Use it whenever you are building a tool that others (or your future self) will use.

Reading from stdin for Piping

Unix-style piping lets you chain programs together. Python can read from sys.stdin to accept piped input, making your scripts composable with other tools.

import sys

line_count = 0
word_count = 0

for line in sys.stdin:
    line_count += 1
    word_count += len(line.split())

print(f"Lines: {line_count}, Words: {word_count}")

Usage from the terminal:

# Terminal
$ cat myfile.txt | python word_counter.py
Lines: 42, Words: 318

$ echo "hello world" | python word_counter.py
Lines: 1, Words: 2

A useful pattern is to support both piped input and interactive input:

import sys

if not sys.stdin.isatty():
    # Input is being piped
    data = sys.stdin.read()
else:
    # Interactive mode
    data = input("Enter your text: ")

print(f"You provided {len(data)} characters.")

Password Input with getpass

When asking for passwords or other sensitive data, you should never echo the characters back to the screen. The getpass module handles this for you.

import getpass

username = input("Username: ")
password = getpass.getpass("Password: ")

# The password is NOT displayed as the user types
if username == "admin" and password == "secret123":
    print("Access granted.")
else:
    print("Access denied.")

Note: getpass may not work correctly in some IDEs or notebook environments. It is designed for terminal usage.

Practical Examples

Example 1: Simple Calculator

def calculator():
    """A simple calculator that takes two numbers and an operator."""
    print("=== Simple Calculator ===")

    try:
        num1 = float(input("Enter first number: "))
        operator = input("Enter operator (+, -, *, /): ").strip()
        num2 = float(input("Enter second number: "))
    except ValueError:
        print("Error: Please enter valid numbers.")
        return

    if operator == "+":
        result = num1 + num2
    elif operator == "-":
        result = num1 - num2
    elif operator == "*":
        result = num1 * num2
    elif operator == "/":
        if num2 == 0:
            print("Error: Division by zero is not allowed.")
            return
        result = num1 / num2
    else:
        print(f"Error: Unknown operator.")
        return

    print(f"{num1} {operator} {num2} = {result}")

calculator()

Example 2: Menu-Driven Program

def todo_app():
    """A menu-driven to-do list that loops until the user quits."""
    tasks = []

    while True:
        print("\n=== To-Do List ===")
        print("1. View tasks")
        print("2. Add task")
        print("3. Remove task")
        print("4. Quit")

        choice = input("Choose an option (1-4): ").strip()

        if choice == "1":
            if not tasks:
                print("No tasks yet.")
            else:
                for i, task in enumerate(tasks, 1):
                    print(f"  {i}. {task}")

        elif choice == "2":
            task = input("Enter task description: ").strip()
            if task:
                tasks.append(task)
                print(f"Added: {task}")
            else:
                print("Error: Task cannot be empty.")

        elif choice == "3":
            if not tasks:
                print("No tasks to remove.")
                continue
            try:
                index = int(input(f"Enter task number to remove (1-{len(tasks)}): "))
                if 1 <= index <= len(tasks):
                    removed = tasks.pop(index - 1)
                    print(f"Removed: {removed}")
                else:
                    print("Error: Invalid task number.")
            except ValueError:
                print("Error: Please enter a valid number.")

        elif choice == "4":
            print("Goodbye!")
            break

        else:
            print("Error: Please choose 1, 2, 3, or 4.")

todo_app()

Example 3: A Simple Quiz Game

def quiz_game():
    """A trivia quiz that scores the answers."""
    questions = [
        {
            "question": "What keyword defines a function in Python?",
            "options": ["a) func", "b) def", "c) function", "d) define"],
            "answer": "b"
        },
        {
            "question": "Which data type is immutable?",
            "options": ["a) list", "b) dict", "c) tuple", "d) set"],
            "answer": "c"
        },
        {
            "question": "What does len() return for an empty list?",
            "options": ["a) None", "b) -1", "c) 0", "d) Error"],
            "answer": "c"
        },
        {
            "question": "Which module is used for regular expressions?",
            "options": ["a) regex", "b) re", "c) regexp", "d) match"],
            "answer": "b"
        }
    ]

    score = 0
    total = len(questions)

    print("=== Python Trivia Quiz ===\n")

    for i, q in enumerate(questions, 1):
        print(f"Question {i}: {q['question']}")
        for option in q["options"]:
            print(f"  {option}")

        answer = input("Your answer (a/b/c/d): ").strip().lower()

        if answer == q["answer"]:
            print("Correct!\n")
            score += 1
        else:
            print(f"Wrong. The correct answer was '{q['answer']}'.\n")

    percentage = (score / total) * 100
    print(f"Final Score: {score}/{total} ({percentage:.0f}%)")

    if percentage == 100:
        print("Perfect score! Excellent work.")
    elif percentage >= 75:
        print("Great job! You know your Python well.")
    elif percentage >= 50:
        print("Not bad. Keep studying!")
    else:
        print("Keep practicing. You will get there.")

quiz_game()

Example 4: Command-Line Tool with argparse

# file_stats.py - A command-line tool that reports statistics about a text file.
# Usage: python file_stats.py myfile.txt --verbose

import argparse
import os

def get_file_stats(filepath, verbose=False):
    """Read a file and return its statistics."""
    if not os.path.exists(filepath):
        print(f"Error: File not found.")
        return

    with open(filepath, "r") as f:
        content = f.read()

    lines = content.split("\n")
    words = content.split()
    characters = len(content)

    print(f"File: {filepath}")
    print(f"  Lines:      {len(lines)}")
    print(f"  Words:      {len(words)}")
    print(f"  Characters: {characters}")

    if verbose:
        unique_words = set(word.lower().strip(".,!?;:") for word in words)
        print(f"  Unique words: {len(unique_words)}")
        avg_line_len = characters / len(lines) if lines else 0
        print(f"  Avg line length: {avg_line_len:.1f} characters")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Display statistics about a text file.")
    parser.add_argument("file", help="Path to the text file")
    parser.add_argument("-v", "--verbose", action="store_true",
                        help="Show additional statistics")

    args = parser.parse_args()
    get_file_stats(args.file, args.verbose)

Security Considerations

User input is one of the most common attack vectors in software. Treating all input as potentially malicious is not paranoia — it is good engineering.

Never use eval() on user input

This is arguably the most dangerous mistake in Python. eval() executes arbitrary Python expressions, which means a malicious user can run any code on your system:

# DANGEROUS - Never do this
user_input = input("Enter a math expression: ")
result = eval(user_input)  # User could type: __import__('os').system('rm -rf /')

# SAFE alternative - use the ast module for math expressions
import ast

def safe_eval_math(expression):
    """Safely evaluate a simple math expression."""
    try:
        tree = ast.parse(expression, mode='eval')
        # Only allow numbers and basic math operators
        for node in ast.walk(tree):
            if not isinstance(node, (ast.Expression, ast.BinOp, ast.UnaryOp,
                                     ast.Constant, ast.Add, ast.Sub,
                                     ast.Mult, ast.Div, ast.Pow,
                                     ast.USub)):
                raise ValueError("Unsupported operation")
        return eval(compile(tree, "<string>", "eval"))
    except (ValueError, SyntaxError):
        return None

result = safe_eval_math(input("Enter a math expression: "))
if result is not None:
    print(f"Result: {result}")
else:
    print("Error: Invalid expression.")

Sanitize input before using it in file paths, SQL queries, or shell commands

# DANGEROUS - path traversal attack
filename = input("Enter filename: ")
# User types: ../../etc/passwd

# SAFE - validate and sanitize
import os

filename = input("Enter filename: ").strip()
# Remove any path separators
safe_name = os.path.basename(filename)
# Verify it is within the expected directory
filepath = os.path.join("/data", safe_name)
filepath = os.path.realpath(filepath)

if not filepath.startswith("/data/"):
    print("Error: Access denied.")
else:
    with open(filepath, "r") as f:
        print(f.read())

Never pass user input directly to os.system() or subprocess with shell=True

import subprocess
import os

# DANGEROUS
user_dir = input("Directory to list: ")
os.system(f"ls {user_dir}")  # User types: /tmp; rm -rf /

# SAFE - use subprocess with a list of arguments (no shell interpretation)
user_dir = input("Directory to list: ")
subprocess.run(["ls", user_dir], check=True)

Common Pitfalls

These are mistakes that developers encounter repeatedly when dealing with user input.

1. Forgetting type conversion

# Bug: string concatenation instead of addition
age = input("Enter your age: ")  # Returns "25" (a string)
print(age + 10)  # TypeError: can only concatenate str to str

# Fix: convert to int
age = int(input("Enter your age: "))
print(age + 10)  # 35

2. Not handling empty input

# Bug: crashes on empty input
number = int(input("Enter a number: "))  # User just presses Enter
# ValueError: invalid literal for int() with base 10: ''

# Fix: check before converting
raw = input("Enter a number: ").strip()
if raw:
    number = int(raw)
else:
    print("No input provided.")

3. EOFError when input is piped or exhausted

# Bug: crashes when stdin is closed (e.g., in automated testing)
name = input("Name: ")  # EOFError if no stdin available

# Fix: handle EOFError
try:
    name = input("Name: ")
except EOFError:
    name = "default"
    print("No input stream available, using default.")

4. Not stripping whitespace

# Bug: trailing spaces cause comparison to fail
choice = input("Type yes to continue: ")
if choice == "yes":  # Fails if user typed "yes " with a trailing space
    print("Continuing...")

# Fix: always strip
choice = input("Type yes to continue: ").strip().lower()
if choice == "yes":
    print("Continuing...")

5. Assuming input encoding

# In Python 3, input() returns a Unicode string by default.
# However, if reading from a file or pipe, encoding issues can arise.
import sys

# Set explicit encoding when needed
sys.stdin.reconfigure(encoding='utf-8')

Best Practices

After years of writing Python professionally, these are the habits that pay off consistently:

  1. Always validate input. Never assume users will provide correct data. Validate type, range, format, and length.
  2. Provide clear, specific prompts. Instead of input(": "), write input("Enter your age (1-120): "). Tell users exactly what you expect.
  3. Handle edge cases. Empty strings, extremely large numbers, special characters, and Unicode input all need to be considered.
  4. Use functions for reusable input patterns. If you ask for validated integers in multiple places, write a get_valid_int() function once and reuse it.
  5. Prefer argparse over sys.argv for command-line tools. It gives you help text, type checking, and error handling for free.
  6. Use getpass for sensitive data. Never display passwords on the screen.
  7. Strip whitespace. Call .strip() on virtually every input() call to avoid invisible whitespace bugs.
  8. Normalize case for comparisons. Use .lower() or .upper() before comparing text input so “Yes”, “YES”, and “yes” all work.
  9. Set timeouts for critical applications. For network services or long-running tools, consider using signal or threading to set input timeouts so your program does not hang indefinitely.
  10. Log input for debugging, but never log passwords. In production systems, log what users entered (sanitized) to help diagnose issues, but scrub any sensitive fields.

Key Takeaways

  • input() always returns a string. Convert explicitly with int(), float(), or other type constructors when you need a different type.
  • Wrap type conversions in try/except blocks and use while loops for a retry pattern that does not crash on bad input.
  • Use split() and map() to collect multiple values from a single line of input.
  • For command-line tools, argparse is the professional choice — it handles parsing, validation, and help generation automatically.
  • Read from sys.stdin when you want your script to work with Unix pipes and redirects.
  • Use getpass to securely collect passwords without echoing them to the screen.
  • Never use eval() on user input. Never pass raw input to shell commands. Always sanitize input before using it in file paths or database queries.
  • Professional code validates everything: type, range, format, length, and encoding. If validation fails, provide a clear error message and give the user another chance.

Source code on Github




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *