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
323: String Interning and Identity
I was digging through a codebase recently and stumbled across a bug that looked absolutely impossible. Someone was using the is operator to compare two strings, and it worked perfectly in their local tests, but failed randomly in production. When I first saw it, I thought, "Who does that? Just use ==." But it got me thinking about why it worked at all, and that leads us directly into the weird world of string interning.
The Difference Between Same and Identical
First, let's clear the air on == versus is. == checks for equality—do these two things have the same value? is checks for identity—are these two things actually the exact same object in memory?
Let's try a quick experiment. I'll define two strings that are identical literals.
# Let's use a common config key as our example
key1 = "MAX_CONNECTION_RETRIES"
key2 = "MAX_CONNECTION_RETRIES"
print(key1 == key2) # True
print(key1 is key2) # True
Wait. Why is is True? I just defined two different variables. You'd expect Python to create two different string objects in memory, right? But it didn't. Python noticed these were the same constant string, so it just pointed both variables to the same memory address to save space. This is called interning.
When the Magic Stops Working
Now, here is where the bug I found actually lives. Interning doesn't happen for every string. It usually happens for constants that look like identifiers (letters, numbers, underscores). But if we build a string dynamically at runtime, Python's "automatic" interning often takes a backseat.
Let's try to build the same string using concatenation:
part1 = "MAX_CONNECTION"
part2 = "_RETRIES"
key3 = part1 + part2
print(key3 == key1) # True (The values are the same)
print(key3 is key1) # False (The identity is different!)
This is the "gotcha." To the human eye, key3 and key1 are identical. == tells us they have the same content. But is tells us they are different objects living in different spots in RAM. If you're using is for logic in your app, your code will pass when you use hardcoded strings but crash the moment those strings are loaded from a file or concatenated together.
Taking Control with sys.intern
You might be wondering: "If interning saves memory, why doesn't Python just do it for everything?" Well, checking every single string created during a program's lifecycle against a global table of interned strings would actually slow the program down. There's a trade-off between memory and CPU.
However, if you know you have a set of strings that will be repeated thousands of times (like keys in a massive dictionary of configuration settings), you can force Python to intern them using the sys module.
import sys
# Dynamic strings that aren't automatically interned
dynamic_key_a = "DATABASE_URL"
dynamic_key_b = "".join(["DATABASE", "_", "URL"])
print(dynamic_key_a is dynamic_key_b) # False
# Now we force them into the intern table
interned_a = sys.intern(dynamic_key_a)
interned_b = sys.intern(dynamic_key_b)
print(interned_a is interned_b) # True
By using sys.intern(), we're telling Python: "I don't care how this string was made; if you already have a copy of this value in the intern table, use that one." This is a powerful optimization for memory-heavy applications, but as a general rule of thumb for your daily coding: unless you are specifically optimizing for memory or working with singletons, stick to == for strings. It'll save you from the kind of debugging nightmare I had to deal with last week.
📋 Practical Task
Fixing the Identity Crisis in a Config Validator
You've been handed a piece of code for a configuration validator. The original author tried to be "clever" by using identity checks (is) for performance, but it's currently failing because the configuration keys are being read from a simulated file (as a list of strings) rather than being hardcoded.
Your Goal: Modify the validate_key function so that it correctly identifies the keys using identity (is), but ensure the strings are properly handled so that the identity check actually works.
import sys
def validate_key(input_key):
# The author wants to use 'is' for a micro-optimization
# but it's currently returning False for dynamic strings.
TARGET_KEY = "API_VERSION_SECRET"
# TODO: Ensure input_key is interned before the comparison
if input_key is TARGET_KEY:
return "Access Granted"
else:
return "Access Denied"
# Simulated input from a config file (these are dynamic strings)
user_input = "".join(["API", "_VERSION", "_SECRET"])
print(f"Result: {validate_key(user_input)}")
# This currently prints "Access Denied".
# Make it print "Access Granted" by fixing the function.
There are no comments for now.