The __getattr__ method in Python allows you to define custom behavior for attribute access. It's called when an attribute that does not exist is accessed. This is useful for dynamic attribute handling, like creating "virtual" attributes, or logging attribute access.
Here are 10 Python code snippets demonstrating various ways to use __getattr__:
1. Basic Usage of __getattr__ for Undefined Attributes
classMyClass:def__getattr__(self,name):returnf"Attribute '{name}' not found!"obj =MyClass()print(obj.some_attribute)# This will call __getattr__ since 'some_attribute' does not exist
2. Handling Default Values with __getattr__
You can use __getattr__ to provide default values when an attribute is accessed but does not exist.
classMyClass:def__getattr__(self,name):return"Default Value"obj =MyClass()print(obj.non_existent)# Output will be "Default Value"
3. Simulating Read-Only Attributes with __getattr__
You can use __getattr__ to simulate read-only attributes by intercepting the attribute access.
4. Creating Virtual Attributes with __getattr__
You can dynamically generate virtual attributes that don't actually exist.
5. Logging Attribute Access with __getattr__
You can log or track attribute access.
6. Accessing Non-Existent Attributes for Method Call Simulation
You can simulate method calls for attributes that are not defined.
7. Raising Custom Exceptions with __getattr__
You can raise custom exceptions when accessing certain attributes.
8. Accessing Nested Attributes Dynamically
You can use __getattr__ to implement dynamic access to nested attributes, useful in cases like working with JSON-like structures.
9. Using __getattr__ for Dynamic Method Resolution
You can use __getattr__ for resolving method names dynamically.
10. Caching Computed Attributes Using __getattr__
You can cache computed values in the __getattr__ method to improve performance.
These examples demonstrate the versatility of __getattr__ in handling dynamic behavior for attribute access, from providing default values, simulating methods, to handling nested structures and caching.
class MyClass:
def __init__(self, value):
self._value = value
def __getattr__(self, name):
if name == 'value':
return self._value
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass(42)
print(obj.value) # Output: 42
class MyClass:
def __init__(self, multiplier):
self._multiplier = multiplier
def __getattr__(self, name):
if name == 'computed_value':
return 10 * self._multiplier
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass(5)
print(obj.computed_value) # Output: 50
class MyClass:
def __getattr__(self, name):
print(f"Accessed attribute: {name}")
return f"Attribute '{name}'"
obj = MyClass()
print(obj.some_attribute) # Logs the access to 'some_attribute'
class MyClass:
def __getattr__(self, name):
def method(*args, **kwargs):
return f"Called method '{name}' with args: {args} and kwargs: {kwargs}"
return method
obj = MyClass()
print(obj.some_method(1, 2, 3)) # Simulates a method call on 'some_method'
class MyClass:
def __getattr__(self, name):
if name == "restricted":
raise ValueError("Access to 'restricted' attribute is not allowed!")
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass()
try:
print(obj.restricted) # Will raise a ValueError
except ValueError as e:
print(e)
class MyClass:
def __init__(self, data):
self.data = data
def __getattr__(self, name):
if name in self.data:
return self.data[name]
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass({'name': 'Alice', 'age': 30})
print(obj.name) # Output: Alice
class MyClass:
def __getattr__(self, name):
if name.startswith('method_'):
return lambda: f"Executing dynamic method {name}"
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass()
print(obj.method_one()) # Output: Executing dynamic method method_one
print(obj.method_two()) # Output: Executing dynamic method method_two
class MyClass:
def __init__(self, value):
self._value = value
self._cached_value = None
def __getattr__(self, name):
if name == 'cached_value':
if self._cached_value is None:
print("Computing value...")
self._cached_value = self._value * 10
return self._cached_value
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
obj = MyClass(5)
print(obj.cached_value) # Output: Computing value... 50
print(obj.cached_value) # Output: 50, cached without recomputation