{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Syntactic Sugar Cheatsheet\n",
    "\n",
    "A comprehensive reference for Python's most useful syntactic sugar patterns that make code more readable, efficient, and Pythonic.\n",
    "\n",
    "## 🟢 Essential Patterns (Everyone Should Know)\n",
    "\n",
    "### List Comprehensions\n",
    "\n",
    "**When to use:** Transform or filter a list of items\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "[expression for item in list]\n",
    "```\n",
    "\n",
    "**Simple Examples:**\n",
    "```python\n",
    "# Transform each number\n",
    "numbers = [1, 2, 3, 4, 5]\n",
    "squares = [x * x for x in numbers]\n",
    "# Result: [1, 4, 9, 16, 25]\n",
    "\n",
    "# Filter with condition\n",
    "even_numbers = [x for x in numbers if x % 2 == 0]\n",
    "# Result: [2, 4]\n",
    "\n",
    "# Instead of this loop:\n",
    "result = []\n",
    "for x in numbers:\n",
    "    if x % 2 == 0:\n",
    "        result.append(x * x)\n",
    "\n",
    "# Write this:\n",
    "result = [x * x for x in numbers if x % 2 == 0]\n",
    "```\n",
    "\n",
    "### Dictionary Comprehensions\n",
    "\n",
    "**When to use:** Build dictionaries from existing data\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "{key: value for item in list}\n",
    "```\n",
    "\n",
    "**Simple Examples:**\n",
    "```python\n",
    "# Create word lengths dictionary\n",
    "words = ['cat', 'dog', 'elephant']\n",
    "lengths = {word: len(word) for word in words}\n",
    "# Result: {'cat': 3, 'dog': 3, 'elephant': 8}\n",
    "\n",
    "# Transform values in existing dictionary\n",
    "prices = {'apple': 1.20, 'banana': 0.80}\n",
    "rounded = {fruit: round(price) for fruit, price in prices.items()}\n",
    "# Result: {'apple': 1, 'banana': 1}\n",
    "```\n",
    "\n",
    "### F-strings (Python 3.6+)\n",
    "\n",
    "**When to use:** Insert variables into strings (better than `.format()` or `%`)\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "f\"Text {variable} more text\"\n",
    "```\n",
    "\n",
    "**Simple Examples:**\n",
    "```python\n",
    "name = \"Alice\"\n",
    "age = 25\n",
    "\n",
    "# Basic variable insertion\n",
    "message = f\"Hello {name}!\"\n",
    "# Result: \"Hello Alice!\"\n",
    "\n",
    "# Multiple variables\n",
    "info = f\"{name} is {age} years old\"\n",
    "# Result: \"Alice is 25 years old\"\n",
    "\n",
    "# Simple expressions\n",
    "status = f\"Adult: {age >= 18}\"\n",
    "# Result: \"Adult: True\"\n",
    "\n",
    "# Number formatting\n",
    "price = 19.99\n",
    "formatted = f\"Price: ${price:.2f}\"\n",
    "# Result: \"Price: $19.99\"\n",
    "```\n",
    "\n",
    "### Basic Unpacking\n",
    "\n",
    "**When to use:** Get multiple values from lists/tuples at once\n",
    "\n",
    "**Simple Examples:**\n",
    "```python\n",
    "# Split coordinates\n",
    "point = [10, 20]\n",
    "x, y = point\n",
    "# x = 10, y = 20\n",
    "\n",
    "# Swap variables (no temp variable needed!)\n",
    "a, b = 5, 3\n",
    "a, b = b, a  # Now a = 3, b = 5\n",
    "\n",
    "# Get function results\n",
    "def get_name_age():\n",
    "    return \"Alice\", 25\n",
    "\n",
    "name, age = get_name_age()\n",
    "```\n",
    "\n",
    "### Basic zip() and enumerate()\n",
    "\n",
    "**When to use:** Combine lists or get index+value pairs\n",
    "\n",
    "**zip() - Combine lists:**\n",
    "```python\n",
    "names = ['Alice', 'Bob', 'Charlie']\n",
    "ages = [25, 30, 35]\n",
    "\n",
    "# Process pairs together\n",
    "for name, age in zip(names, ages):\n",
    "    print(f\"{name}: {age}\")\n",
    "\n",
    "# Create dictionary from two lists\n",
    "people = dict(zip(names, ages))\n",
    "# Result: {'Alice': 25, 'Bob': 30, 'Charlie': 35}\n",
    "```\n",
    "\n",
    "**enumerate() - Get index and value:**\n",
    "```python\n",
    "items = ['apple', 'banana', 'cherry']\n",
    "\n",
    "# Get position and item\n",
    "for i, item in enumerate(items):\n",
    "    print(f\"{i}: {item}\")\n",
    "# Output: 0: apple, 1: banana, 2: cherry\n",
    "\n",
    "# Start counting from 1\n",
    "for i, item in enumerate(items, 1):\n",
    "    print(f\"{i}. {item}\")\n",
    "# Output: 1. apple, 2. banana, 3. cherry\n",
    "```\n",
    "\n",
    "### Simple Conditional Expressions\n",
    "\n",
    "**When to use:** Choose between two values based on a condition\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "value_if_true if condition else value_if_false\n",
    "```\n",
    "\n",
    "**Simple Examples:**\n",
    "```python\n",
    "age = 20\n",
    "status = \"adult\" if age >= 18 else \"minor\"\n",
    "\n",
    "# In lists\n",
    "numbers = [1, -2, 3, -4, 5]\n",
    "absolute = [x if x >= 0 else -x for x in numbers]\n",
    "# Result: [1, 2, 3, 4, 5]\n",
    "\n",
    "# Better than if/else for simple cases\n",
    "# Instead of:\n",
    "if score >= 60:\n",
    "    grade = \"Pass\"\n",
    "else:\n",
    "    grade = \"Fail\"\n",
    "\n",
    "# Write:\n",
    "grade = \"Pass\" if score >= 60 else \"Fail\"\n",
    "```\n",
    "\n",
    "## 🟡 Common Patterns (Useful in Most Projects)\n",
    "\n",
    "### Generator Expressions\n",
    "\n",
    "**When to use:** Process large amounts of data without storing everything in memory\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "(expression for item in list)\n",
    "```\n",
    "\n",
    "**Key Difference:** Uses `()` instead of `[]` - creates items one at a time\n",
    "\n",
    "```python\n",
    "# For large data sets - saves memory\n",
    "large_numbers = range(1000000)\n",
    "\n",
    "# List comprehension - stores all million numbers\n",
    "squares_list = [x * x for x in large_numbers]  # Uses lots of memory\n",
    "\n",
    "# Generator expression - creates one at a time\n",
    "squares_gen = (x * x for x in large_numbers)   # Uses little memory\n",
    "\n",
    "# Use generators with functions that process items one by one\n",
    "total = sum(x * x for x in large_numbers)\n",
    "max_value = max(x * x for x in range(100))\n",
    "\n",
    "# Process large files efficiently\n",
    "def process_large_file(filename):\n",
    "    with open(filename) as f:\n",
    "        # Generator - doesn't load entire file into memory\n",
    "        lines = (line.strip().upper() for line in f)\n",
    "        return sum(1 for line in lines if 'ERROR' in line)\n",
    "```\n",
    "\n",
    "### Set Comprehensions\n",
    "\n",
    "**When to use:** Get unique values or remove duplicates while transforming\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "{expression for item in list}\n",
    "```\n",
    "\n",
    "```python\n",
    "# Get unique lengths\n",
    "words = ['cat', 'dog', 'cat', 'elephant', 'dog']\n",
    "unique_lengths = {len(word) for word in words}\n",
    "# Result: {3, 8} - only unique values\n",
    "\n",
    "# Remove duplicates while transforming\n",
    "numbers = [1, 2, 2, 3, 3, 4]\n",
    "unique_squares = {x * x for x in numbers}\n",
    "# Result: {1, 4, 9, 16}\n",
    "```\n",
    "\n",
    "### Advanced Unpacking with *\n",
    "\n",
    "**When to use:** Handle lists of unknown length or skip unwanted values\n",
    "\n",
    "```python\n",
    "# Get first, last, and everything in between\n",
    "scores = [95, 87, 92, 78, 88, 91]\n",
    "first, *middle, last = scores\n",
    "# first = 95, middle = [87, 92, 78, 88], last = 91\n",
    "\n",
    "# Skip values you don't need\n",
    "data = ['header', 'value1', 'value2', 'value3', 'footer']\n",
    "header, *_, footer = data\n",
    "# header = 'header', footer = 'footer' (_ means \"ignore these\")\n",
    "\n",
    "# Function with flexible arguments\n",
    "def greet(name, *hobbies):\n",
    "    print(f\"Hi {name}!\")\n",
    "    if hobbies:\n",
    "        print(f\"I see you like: {', '.join(hobbies)}\")\n",
    "\n",
    "greet(\"Alice\", \"reading\", \"hiking\", \"coding\")\n",
    "\n",
    "# Unpack when calling functions\n",
    "coordinates = [10, 20, 30]\n",
    "print(*coordinates)  # Same as print(10, 20, 30)\n",
    "```\n",
    "\n",
    "### The with Statement (Context Managers)\n",
    "\n",
    "**When to use:** Work with files, databases, or anything that needs cleanup\n",
    "\n",
    "**Why it's better:** Automatically handles closing/cleanup even if errors occur\n",
    "\n",
    "```python\n",
    "# File handling - file automatically closes\n",
    "with open('data.txt') as file:\n",
    "    content = file.read()\n",
    "# File is guaranteed to be closed here, even if an error occurred\n",
    "\n",
    "# Multiple files at once\n",
    "with open('input.txt') as infile, open('output.txt', 'w') as outfile:\n",
    "    data = infile.read()\n",
    "    outfile.write(data.upper())\n",
    "# Both files automatically closed\n",
    "\n",
    "# Instead of this error-prone code:\n",
    "file = open('data.txt')\n",
    "try:\n",
    "    content = file.read()\n",
    "finally:\n",
    "    file.close()  # Might forget this!\n",
    "\n",
    "# Use this:\n",
    "with open('data.txt') as file:\n",
    "    content = file.read()\n",
    "```\n",
    "\n",
    "### Walrus Operator := (Python 3.8+)\n",
    "\n",
    "**When to use:** Assign and use a value in the same line (avoid repeating expensive operations)\n",
    "\n",
    "**Basic Pattern:**\n",
    "```python\n",
    "if (variable := expression):\n",
    "    # use variable\n",
    "```\n",
    "\n",
    "```python\n",
    "# Avoid calling expensive function twice\n",
    "# Instead of:\n",
    "if len(expensive_computation()) > 10:\n",
    "    print(f\"Got {len(expensive_computation())} items\")  # Called twice!\n",
    "\n",
    "# Write:\n",
    "if (n := len(expensive_computation())) > 10:\n",
    "    print(f\"Got {n} items\")  # Called only once\n",
    "\n",
    "# Useful in while loops\n",
    "numbers = []\n",
    "while (user_input := input(\"Enter number (or 'done'): \")) != \"done\":\n",
    "    numbers.append(int(user_input))\n",
    "\n",
    "# Check and use in one line\n",
    "import re\n",
    "text = \"Phone: 123-456-7890\"\n",
    "if (match := re.search(r'\\d{3}-\\d{3}-\\d{4}', text)):\n",
    "    phone = match.group()\n",
    "    print(f\"Found phone: {phone}\")\n",
    "```\n",
    "\n",
    "### Slice Notation\n",
    "\n",
    "**When to use:** Get parts of lists, strings, or other sequences\n",
    "\n",
    "```python\n",
    "data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "# Basic slicing\n",
    "first_three = data[:3]        # [0, 1, 2]\n",
    "last_three = data[-3:]       # [7, 8, 9]\n",
    "middle = data[2:7]           # [2, 3, 4, 5, 6]\n",
    "\n",
    "# Skip elements\n",
    "every_second = data[::2]     # [0, 2, 4, 6, 8]\n",
    "every_third = data[::3]      # [0, 3, 6, 9]\n",
    "\n",
    "# Reverse\n",
    "reversed_data = data[::-1]   # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]\n",
    "\n",
    "# Copy a list (shallow copy)\n",
    "copy = data[:]               # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "# Remove first and last\n",
    "without_ends = data[1:-1]    # [1, 2, 3, 4, 5, 6, 7, 8]\n",
    "```\n",
    "\n",
    "## 🔴 Advanced Patterns (Specialized Use Cases)\n",
    "\n",
    "### Pattern Matching (Python 3.10+)\n",
    "\n",
    "**When to use:** Complex conditional logic that's clearer than many if/elif statements\n",
    "\n",
    "**Why it's useful:** More readable than long if/elif chains, especially for structured data\n",
    "\n",
    "```python\n",
    "# Instead of many if/elif statements\n",
    "def handle_user_data(data):\n",
    "    match data:\n",
    "        # Match exact values\n",
    "        case {\"status\": \"active\", \"role\": \"admin\"}:\n",
    "            return \"Admin user is active\"\n",
    "        \n",
    "        # Match with variables (capture values)\n",
    "        case {\"status\": \"active\", \"role\": role}:\n",
    "            return f\"Active {role} user\"\n",
    "        \n",
    "        # Match with conditions\n",
    "        case {\"age\": age} if age < 18:\n",
    "            return \"Minor user\"\n",
    "        \n",
    "        # Match lists/tuples\n",
    "        case [first, *rest] if len(rest) > 5:\n",
    "            return f\"Long list starting with {first}\"\n",
    "        \n",
    "        # Default case\n",
    "        case _:\n",
    "            return \"Unknown user type\"\n",
    "\n",
    "# Practical example - processing API responses\n",
    "def process_api_response(response):\n",
    "    match response:\n",
    "        case {\"error\": {\"code\": 404}}:\n",
    "            return \"Not found\"\n",
    "        case {\"error\": {\"code\": code, \"message\": msg}}:\n",
    "            return f\"Error {code}: {msg}\"\n",
    "        case {\"data\": data, \"count\": count} if count > 0:\n",
    "            return f\"Success: {count} items\"\n",
    "        case {\"data\": []}:\n",
    "            return \"No data available\"\n",
    "        case _:\n",
    "            return \"Unexpected response format\"\n",
    "```\n",
    "\n",
    "### Advanced Built-in Functions\n",
    "\n",
    "**Modern alternatives to older patterns:**\n",
    "\n",
    "```python\n",
    "# Prefer comprehensions over map/filter\n",
    "numbers = [1, 2, 3, 4, 5]\n",
    "\n",
    "# Old style (still works, but less readable)\n",
    "squared = list(map(lambda x: x**2, numbers))\n",
    "evens = list(filter(lambda x: x % 2 == 0, numbers))\n",
    "\n",
    "# Modern style (preferred)\n",
    "squared = [x**2 for x in numbers]\n",
    "evens = [x for x in numbers if x % 2 == 0]\n",
    "\n",
    "# Complex example\n",
    "words = ['hello', 'world', 'python', 'is', 'awesome']\n",
    "\n",
    "# Old style - hard to read\n",
    "result = list(map(str.upper, filter(lambda w: len(w) > 4, words)))\n",
    "\n",
    "# Modern style - clear and readable\n",
    "result = [word.upper() for word in words if len(word) > 4]\n",
    "# Result: ['HELLO', 'WORLD', 'PYTHON', 'AWESOME']\n",
    "```\n",
    "\n",
    "### Custom Context Managers\n",
    "\n",
    "**When to use:** Create reusable patterns for setup/cleanup operations\n",
    "\n",
    "**Simple custom context manager using a class:**\n",
    "\n",
    "```python\n",
    "import time\n",
    "\n",
    "class Timer:\n",
    "    \"\"\"Context manager to time operations\"\"\"\n",
    "    def __enter__(self):\n",
    "        self.start = time.time()\n",
    "        return self\n",
    "    \n",
    "    def __exit__(self, *args):\n",
    "        self.end = time.time()\n",
    "        print(f\"Operation took {self.end - self.start:.2f} seconds\")\n",
    "\n",
    "# Usage\n",
    "with Timer():\n",
    "    # Some slow operation\n",
    "    time.sleep(1)\n",
    "    result = sum(range(1000000))\n",
    "# Automatically prints timing when done\n",
    "\n",
    "# Using contextlib (simpler for basic cases)\n",
    "from contextlib import contextmanager\n",
    "\n",
    "@contextmanager\n",
    "def temporary_setting(setting_name, temp_value):\n",
    "    \"\"\"Temporarily change a setting, then restore it\"\"\"\n",
    "    old_value = get_setting(setting_name)\n",
    "    set_setting(setting_name, temp_value)\n",
    "    try:\n",
    "        yield old_value\n",
    "    finally:\n",
    "        set_setting(setting_name, old_value)\n",
    "\n",
    "# Usage\n",
    "with temporary_setting('debug_mode', True):\n",
    "    # Debug mode is temporarily enabled\n",
    "    run_tests()\n",
    "# Debug mode automatically restored to previous value\n",
    "```\n",
    "\n",
    "## Common Mistakes to Avoid\n",
    "\n",
    "### 1. Generator Exhaustion\n",
    "```python\n",
    "# Problem: Generators can only be used once\n",
    "gen = (x * 2 for x in range(5))\n",
    "list1 = list(gen)  # [0, 2, 4, 6, 8]\n",
    "list2 = list(gen)  # [] - Empty! Generator is exhausted\n",
    "\n",
    "# Solution: Use a list if you need to reuse data\n",
    "data = [x * 2 for x in range(5)]  # Can use multiple times\n",
    "```\n",
    "\n",
    "### 2. Mutable Default Arguments\n",
    "```python\n",
    "# Problem: Default list gets modified\n",
    "def add_item(item, my_list=[]):  # DON'T DO THIS\n",
    "    my_list.append(item)\n",
    "    return my_list\n",
    "\n",
    "list1 = add_item(\"apple\")     # [\"apple\"]\n",
    "list2 = add_item(\"banana\")    # [\"apple\", \"banana\"] - Oops!\n",
    "\n",
    "# Solution: Use None as default\n",
    "def add_item(item, my_list=None):\n",
    "    if my_list is None:\n",
    "        my_list = []\n",
    "    my_list.append(item)\n",
    "    return my_list\n",
    "```\n",
    "\n",
    "### 3. Overly Complex Comprehensions\n",
    "```python\n",
    "# Too complex - hard to read\n",
    "result = [x.strip().upper() for sublist in data \n",
    "          for x in sublist if x and len(x) > 3 \n",
    "          if not x.startswith('#')]\n",
    "\n",
    "# Better - use regular loops for complex logic\n",
    "result = []\n",
    "for sublist in data:\n",
    "    for x in sublist:\n",
    "        if x and len(x) > 3 and not x.startswith('#'):\n",
    "            result.append(x.strip().upper())\n",
    "```\n",
    "\n",
    "## Best Practices\n",
    "\n",
    "1. **Start Simple:** Use basic comprehensions before trying advanced features\n",
    "2. **Readability First:** If a one-liner is hard to understand, use multiple lines\n",
    "3. **Memory Awareness:** Use generators for large datasets\n",
    "4. **F-strings Everywhere:** Replace old `.format()` and `%` formatting\n",
    "5. **Context Managers:** Always use `with` for files and resources\n",
    "6. **Meaningful Names:** Even in comprehensions, use clear variable names\n",
    "\n",
    "## Quick Reference by Use Case\n",
    "\n",
    "| I Want To... | Use This Pattern | Example |\n",
    "|--------------|------------------|---------|\n",
    "| Transform a list | List comprehension | `[x*2 for x in numbers]` |\n",
    "| Filter a list | List comprehension with if | `[x for x in numbers if x > 0]` |\n",
    "| Build a dictionary | Dict comprehension | `{k: v*2 for k, v in data.items()}` |\n",
    "| Format strings | F-strings | `f\"Hello {name}\"` |\n",
    "| Process pairs | zip() | `for a, b in zip(list1, list2):` |\n",
    "| Get index + value | enumerate() | `for i, val in enumerate(items):` |\n",
    "| Handle large data | Generator expression | `(x*2 for x in huge_list)` |\n",
    "| Open files safely | with statement | `with open(file) as f:` |\n",
    "| Avoid repeated calculations | Walrus operator | `if (n := len(data)) > 10:` |\n",
    "\n",
    "---\n",
    "\n",
    "*Remember: The best code is readable code. Use these patterns to make your code clearer, not more complex!*"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
