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/Type Conversion

🔄 Type Conversion — Safe Conversions and Type Hints

Master type hints, isinstance() checks, safe conversion patterns, and the typing module for robust code.


🎯 Type Hints and Annotations

Type hints document what types functions expect and return. Python's type checker mypy can verify them.

# Basic type hints
def greet(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

print(greet("Alice"))  # OK
print(greet(42))       # Type checker warns, but runs fine (no enforcement)

# Multiple parameters
def add(a: int, b: int) -> int:
    return a + b

# Variable type hints
count: int = 0
message: str = "Hello"
items: list = []

# Complex types
from typing import List, Dict, Optional, Union, Tuple

def process_names(names: List[str]) -> Dict[str, int]:
    """Map names to their lengths."""
    return {name: len(name) for name in names}

def find_item(items: List[str], target: str) -> Optional[int]:
    """Find index of target, or None if not found."""
    try:
        return items.index(target)
    except ValueError:
        return None

# Union types: accepts multiple types
def stringify(value: Union[int, float, str]) -> str:
    return str(value)

# Tuple with fixed types
def get_coordinates() -> Tuple[float, float]:
    return 3.14, 2.71

💡 The typing Module

from typing import List, Dict, Set, Tuple, Optional, Union, Callable, Any

# Collections
numbers: List[int] = [1, 2, 3]
mapping: Dict[str, int] = {"a": 1, "b": 2}
unique: Set[str] = {"apple", "banana"}
pair: Tuple[str, int] = ("Alice", 30)

# Optional: can be None
user_id: Optional[int] = None
user_id = 42  # OK

# Union: multiple possible types
response: Union[str, int] = "success"
response = 200  # Also OK

# Callable: function types
callback: Callable[[int, int], int] = lambda x, y: x + y
print(callback(3, 4))  # 7

# Any: accept anything (use sparingly)
value: Any = "anything goes"
value = 42
value = [1, 2, 3]

# Generic types (Python 3.9+)
def first_element(items: list[str]) -> str:
    """Return first element from list of strings."""
    return items[0]

# Protocol for structural typing (duck typing with type hints)
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...

class Circle:
    def draw(self) -> None:
        print("Drawing circle")

class Square:
    def draw(self) -> None:
        print("Drawing square")

def draw_shape(shape: Drawable) -> None:
    shape.draw()

draw_shape(Circle())  # OK (has draw method)
draw_shape(Square())  # OK (has draw method)

🎨 isinstance() and Type Checking

# isinstance(): Check if object is instance of class/type
x = 42
print(isinstance(x, int))           # True
print(isinstance(x, (int, float)))  # True (check multiple)

# Type vs isinstance
print(type(x) == int)               # True
print(type(x) is int)               # True (same object)
print(isinstance(x, int))           # True

# Difference with inheritance
class Animal:
    pass

class Dog(Animal):
    pass

d = Dog()
print(type(d) == Dog)               # True
print(type(d) == Animal)            # False
print(isinstance(d, Dog))           # True
print(isinstance(d, Animal))        # True ← isinstance follows inheritance

Use `isinstance()` instead of `type()` for better polymorphism:

# ✗ Wrong: doesn't work with subclasses
def process(obj):
    if type(obj) == list:
        return len(obj)
    elif type(obj) == str:
        return obj.upper()

# ✓ Right: works with subclasses
def process(obj):
    if isinstance(obj, list):
        return len(obj)
    elif isinstance(obj, str):
        return obj.upper()

📊 Safe Type Conversion Patterns

# Pattern 1: Try-except for safe conversion
def safe_int(value):
    try:
        return int(value)
    except ValueError:
        return None

print(safe_int("42"))      # 42
print(safe_int("not_int")) # None

# Pattern 2: Type validation before conversion
def validate_and_convert(value, target_type):
    if isinstance(value, target_type):
        return value  # Already correct type

    if isinstance(value, str):
        try:
            return target_type(value)
        except (ValueError, TypeError):
            raise TypeError(f"Cannot convert {value!r} to {target_type.__name__}")

    raise TypeError(f"Expected str or {target_type.__name__}")

print(validate_and_convert("42", int))     # 42
print(validate_and_convert(42, int))       # 42
# validate_and_convert(42.5, int)          # TypeError

# Pattern 3: Explicit type checking in functions
def process_data(data: Union[List[int], Dict[str, int]]) -> int:
    if isinstance(data, list):
        return sum(data)
    elif isinstance(data, dict):
        return sum(data.values())
    else:
        raise TypeError(f"Unexpected type: {type(data)}")

print(process_data([1, 2, 3]))              # 6
print(process_data({"a": 10, "b": 20}))    # 30

# Pattern 4: Using type() for dynamic conversion
converters = {
    'int': int,
    'float': float,
    'str': str,
    'bool': bool,
}

def flexible_convert(value, type_name):
    if type_name not in converters:
        raise ValueError(f"Unknown type: {type_name}")
    return converters[type_name](value)

print(flexible_convert("42", "int"))       # 42
print(flexible_convert("3.14", "float"))   # 3.14

🔑 Type Checking with mypy

# Install mypy
pip install mypy

# Check your code
mypy script.py

Example with type errors:

# script.py
def add(x: int, y: int) -> int:
    return x + y

result = add(5, "10")  # mypy error: argument is string, not int
print(result)

Running mypy detects the error before execution.


🎯 Best Practices

# ✓ Use type hints in production code
def calculate_age(birth_year: int) -> int:
    from datetime import datetime
    return datetime.now().year - birth_year

# ✓ Use isinstance() for polymorphism
def process(obj: object) -> None:
    if isinstance(obj, str):
        print(obj.upper())
    elif isinstance(obj, list):
        print(f"List with {len(obj)} items")

# ✓ Document type requirements in docstrings
def fetch_data(url: str, timeout: Optional[float] = None) -> Dict[str, Any]:
    """
    Fetch JSON data from URL.

    Args:
        url: The URL to fetch
        timeout: Optional timeout in seconds

    Returns:
        Parsed JSON data as dictionary
    """
    pass

# ✗ Don't mix type checking approaches
def bad_mix(x):  # No type hint
    if type(x) == int:  # Uses type() instead of isinstance()
        return x * 2

# ✓ Use Any sparingly
from typing import Any

def flexible_function(data: Any) -> str:
    """When you truly need to accept anything."""
    return str(data)

🔑 Key Takeaways

ConceptRemember
Type HintsUse for documentation and static checking with mypy
isinstance()Better than type() for polymorphism and inheritance
Try-ExceptEssential for safe runtime conversion
Union & OptionalExpress multiple possible types
ProtocolDuck typing with type safety

🔗 What's Next?

Explore Getting User Input for handling user data safely.


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