Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟢 Beginner🔵 Advanced
What are Variables?Numbers — Integers and FloatsNumber OperationsStrings — Creating and Using TextString FormattingBooleans and NoneType ConversionGetting User InputBest Practices
Python/Variables Data Types/User Input

📥 Getting User Input — Advanced Input Handling and Memory Management

Master safe input handling, duck typing philosophy, object references, and memory management patterns.


🎯 Safe Input Validation and Parsing

import re
from typing import Union, Optional

# Robust input parsing
def get_positive_integer(prompt: str) -> int:
    """Get and validate a positive integer from user."""
    while True:
        try:
            value = int(input(prompt))
            if value <= 0:
                print("Please enter a positive number.")
                continue
            return value
        except ValueError:
            print("Please enter a valid integer.")

# Advanced: Regex validation
def get_email(prompt: str) -> str:
    """Get valid email address."""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    while True:
        email = input(prompt).strip()
        if re.match(pattern, email):
            return email
        print("Invalid email format.")

# Safe choice selection
def choose_option(options: list, prompt: str) -> str:
    """Let user choose from list of options."""
    while True:
        print("\nOptions:")
        for i, option in enumerate(options, 1):
            print(f"{i}. {option}")

        try:
            choice = int(input(prompt))
            if 1 <= choice <= len(options):
                return options[choice - 1]
            print(f"Please enter 1-{len(options)}")
        except ValueError:
            print("Please enter a valid number.")

# Parse CSV input
def get_numbers_list(prompt: str) -> list:
    """Parse comma-separated numbers from user."""
    while True:
        try:
            input_str = input(prompt)
            values = [float(x.strip()) for x in input_str.split(',')]
            return values
        except ValueError:
            print("Please enter valid numbers separated by commas.")

💡 Duck Typing and Protocol-Based Design

Python embraces duck typing: "If it walks like a duck and quacks like a duck, it's a duck."

# Rather than checking types, check for behaviors

# ✗ Type checking approach
def process_sequence_strict(obj):
    if type(obj) == list or type(obj) == tuple:
        return len(obj)
    raise TypeError("Must be list or tuple")

# ✓ Duck typing approach
def process_sequence(obj):
    """Works with any object that has __len__."""
    return len(obj)

# Works with multiple types!
print(process_sequence([1, 2, 3]))      # 3
print(process_sequence((1, 2, 3)))      # 3
print(process_sequence("hello"))        # 5
print(process_sequence({1, 2, 3}))      # 3

# EAFP: Easier to Ask for Forgiveness than Permission
def get_item_eafp(obj, index):
    """EAFP approach: try first, catch errors."""
    try:
        return obj[index]
    except (IndexError, KeyError, TypeError):
        return None

# vs LBYL: Look Before You Leap
def get_item_lbyl(obj, index):
    """LBYL approach: check first."""
    if hasattr(obj, '__getitem__'):
        try:
            return obj[index]
        except (IndexError, KeyError):
            return None
    return None

# EAFP is more Pythonic!

Protocol-based design:

from typing import Protocol

class Drawable(Protocol):
    """Anything with draw() method."""
    def draw(self) -> None: ...

class Saveable(Protocol):
    """Anything with save() method."""
    def save(self) -> None: ...

def draw_all(objects: list[Drawable]) -> None:
    """Draw any objects that have draw()."""
    for obj in objects:
        obj.draw()

# Works with any class implementing draw()
class Circle:
    def draw(self): print("●")

class Square:
    def draw(self): print("■")

draw_all([Circle(), Square()])  # Works!

📊 Object References and Identity

# Identity vs Equality
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)    # True (same content)
print(a is b)    # False (different objects)
print(a is c)    # True (same object)

# id() returns object's memory address
print(id(a))     # Different
print(id(b))     # Different
print(id(c))     # Same as a

# Integers have special caching
x = 5
y = 5
print(x is y)    # True (small ints cached)

x = 300
y = 300
print(x is y)    # False (large ints not cached)

🎨 Mutable vs Immutable Types

# Immutable: int, str, tuple, frozenset
x = 5
y = x
y = 10
print(x)  # Still 5 (y points to new object)

# Mutable: list, dict, set
a = [1, 2, 3]
b = a
b.append(4)
print(a)  # [1, 2, 3, 4] (modified!)

# Consequence: mutable default arguments
def append_to_list(item, items=None):  # ✓ Right
    if items is None:
        items = []
    items.append(item)
    return items

def bad_append(item, items=[]):  # ✗ Wrong: shared!
    items.append(item)
    return items

# Demonstrate the problem
print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2] ← Unexpected!

🔑 Copying Objects

import copy

# Shallow copy: copies outer object, not contents
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)

original[0][0] = 99
print(shallow)  # [[99, 2], [3, 4]] ← Inner list modified!

# Deep copy: recursively copies everything
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)

original[0][0] = 99
print(deep)  # [[1, 2], [3, 4]] ← Not affected

# When to use
def process_data(data):
    """Safe: doesn't modify input."""
    copy_data = copy.deepcopy(data)
    # Modify copy, not original
    copy_data[0] = "modified"
    return copy_data

original = [1, 2, 3]
result = process_data(original)
print(original)  # [1, 2, 3] ← Unchanged

💾 Memory and Garbage Collection

import gc
import sys

# Reference counting
x = [1, 2, 3]
print(sys.getrefcount(x))  # Number of references

y = x  # Increment reference count
print(sys.getrefcount(x))  # Increased

# Garbage collection: automatic when references drop to 0
def create_large_object():
    large_list = [0] * 1_000_000
    return len(large_list)

result = create_large_object()
# large_list automatically freed when function ends

# Circular references
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

a = Node(1)
b = Node(2)
a.next = b
b.next = a  # Circular reference!

# Python's garbage collector handles this
del a, b
gc.collect()  # Explicitly trigger collection

# Memory profiling
from tracemalloc import start, get_traced_memory

start()
data = [i**2 for i in range(100_000)]
current, peak = get_traced_memory()
print(f"Current: {current / 1024:.1f} KB")  # Memory used

🎯 Best Practices

# ✓ Use try-except for input parsing
def safe_get_float(prompt: str) -> Optional[float]:
    try:
        return float(input(prompt))
    except ValueError:
        return None

# ✓ Validate input before using
user_age = get_positive_integer("Enter age: ")
assert 0 < user_age < 150, "Age out of valid range"

# ✓ Use duck typing for flexibility
def count_items(container):
    """Works with any container that has __len__."""
    return len(container)

# ✓ Deep copy when modifying received data
def modify_user_data(user_data):
    safe_copy = copy.deepcopy(user_data)
    safe_copy['modified'] = True
    return safe_copy

# ✗ Don't assume type, use hasattr or duck typing
def process(obj):
    if hasattr(obj, '__len__'):  # Duck typing
        return len(obj)
    return None

# ✗ Don't rely on object identity for values
if x is "hello":  # Wrong
    pass

if x == "hello":  # Right
    pass

🔑 Key Takeaways

ConceptRemember
Input ValidationAlways validate user input before using
Duck TypingCheck behaviors, not types (Pythonic)
ReferencesUnderstanding `is` vs `==` prevents bugs
Mutable DefaultsUse `None` as default for mutable arguments
Deep CopyUse when modifying received objects
Garbage CollectionPython handles automatically via reference counting

🔗 What's Next?

Consolidate your knowledge with Best Practices.


Ready to practice? Challenges | Quiz


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn