Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
314: Literal Types and Final Variables
I've spent a lot of time reviewing code where the "contract" between two functions is basically a pinky-promise. You'll see a function that takes a string for a status or a mode, and if you look at the docstring, it says something like "Must be either 'read' or 'write'". The problem is that the Python interpreter doesn't care about your docstring, and neither does your IDE until the code actually crashes in production because someone typed "reade" instead of "read".
The fragility of implied strings
Let's look at how we usually handle this when we're moving too fast. Imagine we're building a simple file processor. We might write something like this:
def open_connection(mode: str):
if mode == "read":
print("Opening for reading...")
elif mode == "write":
print("Opening for writing...")
else:
raise ValueError(f"Invalid mode: {mode}")
# Somewhere else in the app
open_connection("reade") # Typo!
This code is technically correct, but it's fragile. The str type hint is too broad. It tells the developer (and the linter) that any string is acceptable, but the logic inside the function proves that only two specific strings are actually valid. You're forcing the runtime to do the validation work that your static analysis tools should have caught while you were still typing.
Enforcing specific values with Literal
This is where Literal comes in. Instead of telling Python "this is a string," we tell it "this is exactly one of these specific strings." It shifts the error from a runtime ValueError to a red squiggle in your editor.
from typing import Literal
# We define a type alias to keep things clean
FileMode = Literal["read", "write"]
def open_connection(mode: FileMode):
if mode == "read":
print("Opening for reading...")
elif mode == "write":
print("Opening for writing...")
open_connection("reade") # Your IDE/Mypy will flag this immediately
I prefer this because it serves as living documentation. A new engineer joining the project doesn't have to hunt through the function body to see what the valid options are; they just hover over the FileMode type. It’s a massive win for maintainability, especially when you have five or six valid options instead of just two.
The illusion of constants
While we're tightening up our types, let's talk about constants. In Python, we've all been taught to name things in UPPER_CASE to signal "don't touch this." But as you know, that's just a convention. There is nothing stopping a tired developer from accidentally reassigning a global configuration value halfway through a script.
MAX_RETRIES = 3
def reset_config():
# Someone thinks they're being clever or just makes a mistake
global MAX_RETRIES
MAX_RETRIES = 0
In a small script, this is a nuisance. In a large distributed system, changing a global timeout or retry limit at runtime can cause some truly haunting bugs that are nearly impossible to reproduce in staging.
Locking the door with Final
If you want to tell your teammates (and your future self) that a variable is absolutely not meant to be changed, use Final. It doesn't stop the code from running if you ignore it—Python is still Python—but it tells the type checker to scream if a reassignment is attempted.
from typing import Final
MAX_RETRIES: Final = 3
def reset_config():
MAX_RETRIES = 0 # Type checker error: Cannot assign to Final name "MAX_RETRIES"
I use Final whenever I define API keys, database ports, or mathematical constants. It removes the ambiguity. When I see Final, I know I can rely on that value remaining constant throughout the entire lifecycle of the process without having to scan the rest of the file for rogue assignments.
📋 Practical Task
Refactoring the API Gateway Configuration
You've inherited a piece of code for an API Gateway. It's currently plagued by "magic strings" and accidental constant overrides. Your task is to harden this code using Literal and Final.
Requirements:
- Create a type alias called
GatewayPrioritythat only allows the values"High","Medium", or"Low". - Update the
set_priorityfunction to use this new type. - Mark the
GATEWAY_PORTvariable asFinalto ensure it isn't changed during runtime. - Correct the typo in the function call at the bottom of the script so that it passes type checking.
from typing import Final, Literal
# TODO: Mark this as Final
GATEWAY_PORT = 8080
# TODO: Create the GatewayPriority Literal type here
def set_priority(priority: str):
print(f"Setting gateway priority to {priority}")
# This should be flagged as an error by a type checker
set_priority("Higg")
# Test the port assignment (this should be flagged as an error)
GATEWAY_PORT = 9000
There are no comments for now.