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
263: The string Module's Constants and Templates
By now, you're probably very comfortable with f-strings. They're fast, readable, and usually the right choice. But there are times when you need to define a string pattern before you have the data, or you need a reliable list of characters without typing out the entire alphabet and every single symbol on your keyboard by hand. That's where the string module comes in.
Cleaning up input with string constants
I'm working on a small script that sanitizes user-provided filenames. I want to make sure that any character that isn't a letter or a number gets stripped out. I could manually create a string like "abcdefghijklmnopqrstuvwxyz...", but that's tedious and prone to typos. Instead, I'll use the constants provided by the string module.
import string
def sanitize_filename(filename):
# I want to keep letters and numbers, so I'll combine these constants
allowed = string.ascii_letters + string.digits
# Create a new string keeping only the allowed characters
return "".join(char for char in filename if char in allowed)
print(sanitize_filename("My Report_2023! (Final).txt"))
# Output: MyReport2023Finaltxt
It's clean and explicit. Using string.ascii_letters is much safer than me trying to remember if I missed a character in a manual string.
The "Python 2 hangover" mistake
Here is where I tripped up. I've been coding for a long time, and for a split second, my muscle memory took over. I tried to use string.letters to get the alphabet.
# This is what I wrote initially
import string
print(string.letters)
# AttributeError: module 'string' has no attribute 'letters'
I forgot that string.letters was a Python 2 thing. In Python 3, it was split into string.ascii_lowercase, string.ascii_uppercase, and the combined string.ascii_letters. If you see .letters in an old tutorial, ignore it. Stick to the ascii_ prefix.
Decoupling logic from layout with Templates
Now, let's talk about string.Template. You might wonder why you'd use this when f-strings exist. The key difference is when the string is defined. If you're building an application where a non-developer (like a product manager) needs to edit the wording of an email template in a config file, you can't use f-strings because they require the variables to be in scope at the moment of definition.
I'll set up a simple notification system where the template is separate from the data.
from string import Template
# Imagine this string is loaded from an external .txt or .env file
email_template = Template("Hello ${name}, your order ${order_id} has shipped to ${city}!")
# Now I can apply the data whenever I want
user_data = {
"name": "Alice",
"order_id": "PX-12345",
"city": "Seattle"
}
# safe_substitute is my preference here because it won't crash
# if a key is missing from the dictionary
message = email_template.safe_substitute(user_data)
print(message)
# Output: Hello Alice, your order PX-12345 has shipped to Seattle!
I used safe_substitute instead of substitute. The latter throws a KeyError if a placeholder is missing. In a real-world production environment, crashing your whole app because a "city" field was null in the database is a bad move. safe_substitute just leaves the placeholder as-is (e.g., ${city}) in the output, which is much easier to debug than a crashed server.
📋 Practical Task
Build a Password Complexity Validator
Create a script that validates a password based on character sets. Your program should check a password string and verify that it contains at least one character from each of the following categories using the string module constants:
- At least one lowercase letter (
string.ascii_lowercase) - At least one uppercase letter (
string.ascii_uppercase) - At least one digit (
string.digits) - At least one punctuation symbol (
string.punctuation)
There are no comments for now.