Learn Python Advanced: Master Advanced Concepts
You've mastered intermediate Python. Now let's dive into advanced concepts that separate good programmers from great ones. We'll cover object-oriented programming, advanced data structures, design patterns, and more.
This course assumes you're comfortable with intermediate Python. If you're not, check out our intermediate course first.
Lesson 1: Classes and Objects
Classes let you create your own data types. They're the foundation of object-oriented programming:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
return f"{self.name} says woof!"
def get_info(self):
return f"{self.name} is {self.age} years old"
# Create objects
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
print(dog1.bark()) # Buddy says woof!
print(dog2.get_info()) # Max is 5 years old
Classes organize code and make it reusable. Instead of passing data around, you create objects that contain both data and functions.
Lesson 2: Inheritance
Inheritance lets you create new classes based on existing ones:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement")
class Dog(Animal):
def speak(self):
return f"{self.name} says woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says woof!
print(cat.speak()) # Whiskers says meow!
Inheritance reduces code duplication and makes programs easier to maintain. Child classes inherit from parent classes.
Lesson 3: Magic Methods
Magic methods (dunder methods) let you define how objects behave with built-in operations:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Point({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2
print(p3) # Point(4, 6)
print(p1 == Point(1, 2)) # True
Magic methods make your classes work naturally with Python's built-in operations. They're what make len(), +, == work on your objects.
Lesson 4: Properties and Getters/Setters
Properties let you control how attributes are accessed and modified:
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
temp = Temperature(25)
print(temp.celsius) # 25
print(temp.fahrenheit) # 77.0
temp.celsius = 30
print(temp.fahrenheit) # 86.0
Properties let you add validation and computed values while keeping the interface simple.
Lesson 5: Context Managers
Context managers ensure resources are properly cleaned up. You've used them with files:
# Create your own context manager
class Timer:
def __init__(self):
self.start = None
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, *args):
import time
print(f"Elapsed: {time.time() - self.start:.2f} seconds")
# Use it
with Timer():
# your code here
sum(range(1000000))
Context managers are great for resource management - files, database connections, locks, etc.
Lesson 6: Iterators and Generators
You've seen generators. Now let's understand iterators:
class Countdown:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start + 1
for num in Countdown(5):
print(num) # 5, 4, 3, 2, 1
Understanding iterators helps you understand how Python's for loops work and lets you create custom iterable objects.
Lesson 7: Decorators Deep Dive
Decorators are powerful. Let's understand them better:
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f} seconds")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Done"
slow_function() # Prints: slow_function took 1.00 seconds
Decorators are used everywhere in Python - for logging, caching, authentication, and more. Understanding them opens up a lot of possibilities.
Lesson 8: Metaclasses
Metaclasses are classes that create classes. Advanced topic, but powerful:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=Singleton):
def __init__(self):
print("Creating database connection")
db1 = Database() # Creating database connection
db2 = Database() # (no output - same instance)
print(db1 is db2) # True
Metaclasses are advanced and rarely needed, but they're useful for frameworks and libraries. Most Python programmers never need them.
Lesson 9: Async/Await
Async programming lets you handle multiple tasks concurrently:
import asyncio
async def fetch_data(url):
# Simulate network request
await asyncio.sleep(1)
return f"Data from {url}"
async def main():
tasks = [
fetch_data("url1"),
fetch_data("url2"),
fetch_data("url3")
]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
Async is great for I/O-bound tasks (network requests, file operations). It lets you do multiple things at once without threads.
Lesson 10: Design Patterns
Design patterns are reusable solutions to common problems:
Factory Pattern:
class AnimalFactory:
@staticmethod
def create_animal(animal_type, name):
if animal_type == "dog":
return Dog(name)
elif animal_type == "cat":
return Cat(name)
else:
raise ValueError(f"Unknown animal: {animal_type}")
animal = AnimalFactory.create_animal("dog", "Buddy")
Design patterns help you write better, more maintainable code. Learn them as you encounter problems they solve.
Best Practices
- Follow PEP 8: Python's style guide. Use a linter to check your code.
- Write tests: Test your code. Use unittest or pytest.
- Document your code: Use docstrings to explain what functions and classes do.
- Keep it simple: Don't over-engineer. Simple solutions are often better.
- Read other people's code: Learn from open-source projects.
Pro Tip: Don't try to use every advanced feature. Use them when they solve a real problem. Over-engineering is worse than under-engineering. Start simple, add complexity only when needed.
Common Questions
When should I use classes vs functions?
Use classes when you need to maintain state or when you have related data and functions. Use functions for simple operations. Python is flexible - use what makes sense for your problem.
Do I need to know all these advanced topics?
Not immediately. Learn them as you need them. Most Python code doesn't use metaclasses or advanced decorators. But understanding them makes you a better programmer.
How do I get better at Python?
Write code. Build projects. Read other people's code. Contribute to open source. The more you code, the better you get. There's no shortcut.
Keep Learning and Building
You've learned advanced Python concepts. Now use them. Build projects that use classes, inheritance, and advanced features. Read code from experienced developers. Contribute to open-source projects. The best way to master advanced concepts is to use them in real projects.