-
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
64: Practice Exercise: Building a Password Strength Checker
I've always found that the best way to internalize how to handle strings and loops is to build something that feels like a real-world utility. Today, we're tackling a password strength checker. Now, you could just use a library for this, but that doesn't help us understand the logic. So, let's just start coding and see where we hit a wall.
Just checking the length
My first instinct is usually to start with the most obvious requirement: length. If a password is only three characters long, it doesn't matter if it has a symbol; it's weak. I'll start with a basic function that returns a boolean.
def is_strong(password):
if len(password) >= 8:
return True
return False
print(is_strong("123")) # False
print(is_strong("password123")) # True
This works, but it's useless. "password123" is technically "strong" by this logic, which is ridiculous. I need to track multiple criteria, not just one.
Tracking multiple requirements
Instead of returning True or False immediately, I'll use "flags"—boolean variables that start as False and flip to True once we find what we're looking for. I want to make sure there's at least one uppercase letter and one number.
def is_strong(password):
has_upper = False
has_digit = False
for char in password:
if char.isupper():
has_upper = True
if char.isdigit():
has_digit = True
return len(password) >= 8 and has_upper and has_digit
print(is_strong("password123")) # False (no uppercase)
print(is_strong("Password123")) # True
That's better. But wait—what about special characters? If I try to use something like char.isspecial(), I'll realize quickly that Python doesn't actually have a built-in method for that. This is where we have to get a bit more manual.
Hunting for symbols
Since there's no is_symbol() method, I'll define a string containing all the characters I consider "special." Then I can check if the current character exists within that string using the in keyword.
def is_strong(password):
has_upper = False
has_digit = False
has_special = False
special_chars = "!@#$%^&*()-_=+"
for char in password:
if char.isupper():
has_upper = True
elif char.isdigit():
has_digit = True
elif char in special_chars:
has_special = True
return len(password) >= 8 and has_upper and has_digit and has_special
print(is_strong("Password123")) # False (no symbol)
print(is_strong("Password123!")) # True
I switched to elif here because a character can't be both an uppercase letter and a digit. It's a tiny performance gain, but it's a good habit to have.
From "Yes/No" to a Strength Score
The problem with a boolean return is that it's binary. A password that misses one single symbol is "Weak," while one that hits all four is "Strong." In a real app, you'd want a gradient: Weak, Medium, and Strong. I'll change the flags into a numeric score.
def check_strength(password):
score = 0
special_chars = "!@#$%^&*()-_=+"
if len(password) >= 8: score += 1
# We can use 'any()' here to keep the code cleaner than a full loop
if any(char.isupper() for char in password): score += 1
if any(char.isdigit() for char in password): score += 1
if any(char in special_chars for char in password): score += 1
if score == 4:
return "Strong"
elif score == 3:
return "Medium"
else:
return "Weak"
print(check_strength("Pass123")) # Medium (Len, Upper, Digit)
print(check_strength("Password!")) # Medium (Len, Upper, Special)
print(check_strength("P1!")) # Weak (Upper, Digit, Special - but too short)
I used a generator expression inside any(). It's a very "Pythonic" way of saying: "Check if any character in this string meets this condition." It replaces that whole for loop and the boolean flags we used earlier, making the logic much easier to read at a glance.
📋 Practical Task
Exercise: The Robust Vault Evaluator
Build a password strength evaluator that is more nuanced than the one we explored. Your function should be named evaluate_password_security and must implement the following logic:
- Score 0-1: "Very Weak"
- Score 2: "Weak"
- Score 3: "Moderate"
- Score 4: "Strong"
- Score 5: "Fort Knox"
To reach a score of 5, the password must meet all these criteria:
- Minimum length of 12 characters.
- Contains at least one uppercase letter.
- Contains at least one lowercase letter.
- Contains at least one digit.
- Contains at least one special character from this set:
@#$%^&*()_+-=[]{}|;':",./<>?
Test your function with a variety of inputs (e.g., "12345", "Password123!", "AveryLongPasswordWithNoNumbers") to ensure the scoring logic maps correctly to the descriptions.
There are no comments for now.