🐍 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?
- They don't store the entire sequence in memory.
- They pause execution between
yieldcalls. - Great for streaming data or large file processing.
⚡ 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
- Use
next(gen)to manually get the next item. - Generators raise
StopIterationwhen done. - They can be converted to lists using
list(gen)if needed.
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
- The
@my_decoratorsyntax is shorthand forgreet = my_decorator(greet). - The decorator returns a new function that wraps the original one.
- This allows pre- and post-processing around the target function.
📦 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.
- Indentation: Use
4 spacesper indentation level — never tabs. - Line Length: Limit all lines to a maximum of
79 characters. This makes code more readable on smaller screens and side-by-side diffs. - Blank Lines: Use blank lines to separate:
- Functions and class definitions
- Logical sections inside functions
- Naming Conventions:
snake_casefor functions, variables, and methods (e.g.,calculate_total)CapWords(also known as PascalCase) for class names (e.g.,OrderProcessor)UPPER_CASEfor constants
- Imports: Should be on separate lines and grouped as:
- Standard library imports
- Third-party imports
- Local application imports
- Whitespace: Avoid unnecessary spaces:
- Before commas, colons, or semicolons:
wrong(1 ,2)❌ →right(1, 2)✅ - Around the equals sign in keyword arguments:
foo(bar = 1)❌ →foo(bar=1)✅ - Use a single space around binary operators:
x=1+2❌ →x = 1 + 2✅
- Before commas, colons, or semicolons:
- Docstrings: Use triple double quotes
"""like this"""to describe modules, classes, and functions.
💡 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.