Back to Curriculum

Data Types, Variables, and Memory Management

📚 Lesson 2 of 20 ⏱️ 50 min

Data Types, Variables, and Memory Management

50 min

Python uses dynamic typing, meaning variable types are determined at runtime. This provides flexibility but requires careful attention to type consistency in production code.

Python's built-in data types include immutable types (int, float, str, tuple, frozenset) and mutable types (list, dict, set). Understanding mutability is crucial for avoiding bugs and optimizing performance.

Python uses reference counting for memory management, with automatic garbage collection. Understanding how Python handles memory helps write more efficient code.

Type hints (introduced in Python 3.5) allow you to add optional static typing to your code, improving readability and enabling better IDE support and static analysis tools.

Immutable objects cannot be changed after creation, which makes them safe to use as dictionary keys and in sets. Mutable objects can be modified, which can lead to unexpected behavior if not handled carefully.

Python's garbage collector automatically frees memory when objects are no longer referenced. However, understanding reference cycles and when to explicitly manage resources is important for performance-critical applications.

Key Concepts

  • Python uses dynamic typing - types are determined at runtime.
  • Immutable types (int, float, str, tuple) cannot be modified after creation.
  • Mutable types (list, dict, set) can be modified in place.
  • Type hints provide optional static typing for better code quality.
  • Python uses reference counting and garbage collection for memory management.

Learning Objectives

Master

  • Understanding Python's dynamic typing system
  • Distinguishing between mutable and immutable types
  • Using type hints for better code documentation
  • Understanding Python's memory management model

Develop

  • Type safety thinking and best practices
  • Memory efficiency awareness
  • Understanding Python's object model

Tips

  • Use type hints to document expected types and catch errors early.
  • Prefer immutable types when you don't need to modify data.
  • Be careful when passing mutable objects as function arguments.
  • Use sys.getsizeof() to understand memory usage of objects.

Common Pitfalls

  • Modifying mutable objects passed as function arguments (can cause side effects).
  • Using mutable types as dictionary keys (only immutable types are hashable).
  • Not using type hints, missing opportunities for better IDE support.
  • Creating unnecessary copies of large immutable objects.

Summary

  • Python uses dynamic typing with optional type hints.
  • Understanding mutability is crucial for avoiding bugs.
  • Type hints improve code quality and IDE support.
  • Python's garbage collector manages memory automatically.

Exercise

Create a comprehensive data type demonstration that shows proper type hints, memory considerations, and best practices for variable naming and usage.

from typing import List, Dict, Optional, Union
from dataclasses import dataclass
import sys

# Type hints for better code documentation
def analyze_data_types() -> Dict[str, Union[int, float, str]]:
    """
    Demonstrates various Python data types with proper documentation.
    
    Returns:
        Dictionary containing examples of different data types
    """
    # Immutable types
    user_id: int = 12345
    salary: float = 75000.50
    department: str = "Engineering"
    
    # Mutable types
    skills: List[str] = ["Python", "JavaScript", "SQL"]
    project_info: Dict[str, Union[str, int]] = {
        "name": "Web Application",
        "duration_days": 30,
        "status": "In Progress"
    }
    
    # Memory usage demonstration
    print(f"Memory usage of user_id: {sys.getsizeof(user_id)} bytes")
    print(f"Memory usage of skills list: {sys.getsizeof(skills)} bytes")
    
    # Type checking and validation
    if not isinstance(user_id, int):
        raise TypeError("user_id must be an integer")
    
    return {
        "user_id": user_id,
        "salary": salary,
        "department": department,
        "skills_count": len(skills)
    }

# Data class for structured data
@dataclass
class Employee:
    """Represents an employee with type hints and validation."""
    name: str
    employee_id: int
    salary: float
    department: str
    skills: List[str]
    
    def __post_init__(self):
        """Validate data after initialization."""
        if self.salary < 0:
            raise ValueError("Salary cannot be negative")
        if not self.name.strip():
            raise ValueError("Name cannot be empty")

# Usage example
if __name__ == "__main__":
    try:
        result = analyze_data_types()
        print("Data analysis result:", result)
        
        # Create employee instance
        emp = Employee(
            name="Alice Johnson",
            employee_id=1001,
            salary=85000.0,
            department="Data Science",
            skills=["Python", "Machine Learning", "SQL"]
        )
        print(f"Employee: {emp.name}, Department: {emp.department}")
        
    except Exception as e:
        print(f"Error: {e}")

Exercise Tips

  • Use typing module for complex type hints (List, Dict, Optional).
  • Demonstrate the difference between mutable and immutable operations.
  • Show memory usage with sys.getsizeof() for different data types.
  • Add validation to ensure type safety in your functions.

Code Editor

Output