Chapter Ten Python Program/Notes¶
Chapter 10 - Advanced Attributes¶
There is no way to create a private attribute like in C++. Everything is accessible from everywhere.
You can use underscores and stuff to minimize name collisions. Call that name mangling.
HOWEVER, closures provide a way to create variables that are strictly inaccessible from the outside scope.
def create_counter():
# This variable is 'private' - it cannot be accessed directly
_count = 0
def increment():
nonlocal _count
_count += 1
return _count
def get_count():
return _count
# We only expose the interface, not the variable itself
return {
"increment": increment,
"get_count": get_count
}
counter = create_counter()
print(counter["increment"]()) # Output: 1
print(counter["increment"]()) # Output: 2
print(counter["get_count"]()) # Output: 2
# Attempting to access _count directly is impossible
# print(counter._count) -> AttributeError
By returning a dictionary or a custom object containing functions, you can simulate a class-like structure where the data is truly hidden.
You can also use closure to get a private-ish variable.
You can also make your variable a class, with a display function if you wanna make it that little bit harder to fuck with.
Getters get stuff (duh).
Property objects have methods to be used as decorators.
import os
# ==========================================
# 1. THE CLASS APPROACH (Pythonic "Privacy")
# ==========================================
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # Public
self._internal_id = 12345 # "Protected" (Convention only)
self.__balance = balance # Name Mangled (Harder to access)
# 4. PROPERTIES (Getters and Decorators)
@property
def balance(self):
"""The getter: Allows reading the private __balance."""
print(f"Accessing balance for {self.owner}...")
return f"${self.__balance:.2f}"
@balance.setter
def balance(self, value):
"""The setter: Adds validation logic before changing data."""
if value < 0:
print("Error: You can't have a negative balance!")
else:
self.__balance = value
# ==========================================
# 2. THE CLOSURE APPROACH (Strict Privacy)
# ==========================================
def create_secure_vault(initial_secret):
"""
Demonstrates using a closure to make variables
strictly inaccessible from the outside scope.
"""
_secret_code = initial_secret # Truly private variable
def unlock(attempt):
nonlocal _secret_code
if attempt == _secret_code:
return "Vault Opened!"
return "Access Denied."
def change_code(new_code):
nonlocal _secret_code
_secret_code = new_code
return "Code Updated."
# Exposing only the interface via a dictionary
return {
"unlock": unlock,
"update": change_code
}
# ==========================================
# DEMONSTRATION
# ==========================================
print("--- Part 1: Class Attributes & Properties ---")
acct = BankAccount("Alice", 1000)
# Accessing public and 'protected' (still works, just frowned upon)
print(f"Owner: {acct.owner}")
print(f"Internal ID: {acct._internal_id}")
# Property Access (The Getter)
print(f"Balance: {acct.balance}")
# Name Mangling: This will fail if called directly as acct.__balance
try:
print(acct.__balance)
except AttributeError:
print("Caught AttributeError: acct.__balance is not directly accessible!")
# But Python isn't strictly private; you can still find it if you're determined:
print(f"Mangled Access: {acct._BankAccount__balance}")
print("\n--- Part 2: Closures (True Hiding) ---")
vault = create_secure_vault("Blueberry123")
print(vault["unlock"]("WrongCode")) # Access Denied
print(vault["unlock"]("Blueberry123")) # Vault Opened!
# Note: There is literally no way to 'dot' into the vault to find _secret_code.
# vault._secret_code would throw an AttributeError because 'vault' is a dict.
Key Takeaways from the Implementation:¶
The “Honor System”: In the BankAccount class, the single underscore (_internal_id) is just a sign to other developers saying, “Hey, don’t touch this.” Python won’t actually stop you.
Name Mangling: When we used __balance, Python internally renamed it to _BankAccount__balance. It’s not private; it’s just wearing a disguise.
The Closure “Vault”: This is the “Chapter 10” secret sauce. Because _secret_code exists only in the local scope of create_secure_vault, it is physically impossible to access it from the global scope once the function finishes. You can only interact with it through the functions we “exposed” in the dictionary.
Property Decorators: The @property and @balance.setter allow you to treat a method like a simple variable, which is great for adding “display logic” (like adding a dollar sign) or validation without breaking the user’s ability to just type acct.balance.