🐍 Python Tutorial: Advanced Topics


1. List Comprehensions

List comprehensions provide a concise, Pythonic way to create lists in a single line of code. They are faster and cleaner than using a traditional for loop.

✅ Basic Syntax

[expression for item in iterable]

squares = [x * x for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

⚡ With Conditionals

You can filter items with an if clause at the end.

evens = [x for x in range(20) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

🔁 Nested Loops

You can even use nested loops inside a list comprehension.

pairs = [(x, y) for x in range(3) for y in range(3)]
print(pairs)  # [(0, 0), (0, 1), ..., (2, 2)]

🧠 Conditional Expressions

Use a conditional expression to assign values based on logic.

labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
print(labels)  # ['even', 'odd', 'even', 'odd', 'even']

📦 Real-World Example

Extract the domain names from a list of emails:

emails = ["alice@example.com", "bob@gmail.com"]
domains = [email.split("@")[1] for email in emails]
print(domains)  # ['example.com', 'gmail.com']

2. Generators

Generators allow you to iterate over data lazily, yielding items one by one only as needed. They are memory efficient and perfect for working with large datasets or infinite sequences.

⚙️ Basic Generator Function

A generator function uses the yield keyword instead of return. Each call to next() resumes execution where it last left off.

def countdown(n):
  while n > 0:
    yield n
    n -= 1

for i in countdown(5):
  print(i)  # 5, 4, 3, 2, 1

💡 Why Use Generators?

⚡ Generator Expressions

Similar to list comprehensions but with parentheses instead of square brackets.

squares = (x * x for x in range(5))
print(next(squares))  # 0
print(next(squares))  # 1

🧠 Real-World Example

Reading large files line by line:

def read_large_file(file_path):
  with open(file_path) as file:
    for line in file:
      yield line.strip()

# for line in read_large_file("data.txt"):
#   process(line)

📌 Notes


3. Decorators

Decorators are higher-order functions that take another function and extend or modify its behavior without changing the original function’s code. They are widely used for logging, timing, access control, and more.

🎯 Basic Decorator Example

def my_decorator(func):
  def wrapper():
    print("Before function call")
    func()
    print("After function call")
  return wrapper

@my_decorator
def greet():
  print("Hello!")

greet()
# Output:
# Before function call
# Hello!
# After function call

⚙️ How It Works

📦 Decorators with Arguments

If your function takes arguments, the decorator's inner wrapper must accept them too.

def debug(func):
  def wrapper(*args, **kwargs):
    print(f"Calling {func.__name__} with {args} {kwargs}")
    result = func(*args, **kwargs)
    print(f"{func.__name__} returned {result}")
    return result
  return wrapper

@debug
def add(a, b):
  return a + b

add(3, 4)
# Output:
# Calling add with (3, 4) {}
# add returned 7

🔁 Reusable & Composable

Decorators can be stacked, reused across functions, and even parameterized for flexible behavior.

def shout(func):
  def wrapper():
    result = func()
    return result.upper()
  return wrapper

@shout
def say():
  return "hi there"

print(say())  # HI THERE

🧠 Real Use Case: Timing

import time

def timer(func):
  def wrapper(*args, **kwargs):
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print(f"{func.__name__} took {end - start:.4f}s")
    return result
  return wrapper

@timer
def slow_function():
  time.sleep(1)

slow_function()

4. Writing Pythonic Code

Writing clean, idiomatic Python—also known as “Pythonic” code—means using the language’s features and best practices in a way that is readable, elegant, and efficient.

✅ List Comprehensions

Instead of:

result = []
for x in range(5):
  result.append(x * x)

Do this:

result = [x * x for x in range(5)]

📁 Use with for File I/O

This ensures proper closing of the file:

with open("data.txt") as file:
  content = file.read()

🔁 Multiple Assignment

Swap values without a temporary variable:

a, b = 1, 2
a, b = b, a  # Now a is 2, b is 1

🔢 Use enumerate() and zip()

Instead of using manual counters:

for i in range(len(items)):
          print(i, items[i])

Do this:

for i, item in enumerate(items):
  print(i, item)

5. Follow PEP8 Guidelines

PEP 8 is Python's official style guide, designed to promote readability and consistency across Python codebases. Adhering to PEP 8 helps your code look clean, professional, and easy for others (and your future self) to maintain.

You can use tools like black (for automatic formatting) and flake8 (for linting and style checks) to ensure your code follows these conventions.

💡 Tip: Run black your_file.py or set it up in your editor to auto-format on save. Combine it with flake8 to catch style violations, complexity issues, and unused imports.


Additional Resources & References


← Back : Modules & PackagesNext: Python Standard Library →