Python __setattr__ Method

Complete guide to Python's __setattr__ method covering attribute assignment control, validation, and dynamic attributes.

Python __setattr__ Method

Python setattr Method

Last modified April 8, 2025

This comprehensive guide explores Python’s setattr method, the special method that controls attribute assignment. We’ll cover basic usage, attribute validation, dynamic attributes, and practical examples.

Basic Definitions

The setattr method is called when an attribute assignment is attempted on an object. It intercepts all attribute assignments, including those in init.

Key characteristics: it receives the attribute name and value as arguments, must handle attribute storage manually, and can prevent or transform attribute assignments. It’s called for all attribute assignments.

Basic setattr Implementation

Here’s a simple implementation showing how setattr intercepts attribute assignments. It demonstrates the basic structure and requirements.

basic_setattr.py

class Person: def setattr(self, name, value): print(f"Setting attribute {name} to {value}") super().setattr(name, value)

p = Person() p.name = “Alice” p.age = 30 print(p.name, p.age)

This example shows the basic pattern: print a message, then delegate to the parent class’s setattr using super(). Without this delegation, attributes wouldn’t be stored.

The output shows the interception of both attribute assignments before they’re stored. This is the foundation for more advanced attribute control.

Attribute Validation with setattr

setattr can validate attribute values before allowing them to be set, enforcing business rules or type constraints.

validation.py

class Temperature: def setattr(self, name, value): if name == “celsius”: if not isinstance(value, (int, float)): raise TypeError(“Temperature must be numeric”) if value < -273.15: raise ValueError(“Temperature below absolute zero”) super().setattr(name, value)

temp = Temperature() temp.celsius = 25 # Valid

temp.celsius = -300 # Raises ValueError

temp.celsius = “hot” # Raises TypeError

This temperature class validates that celsius values are numeric and above absolute zero. Invalid assignments raise exceptions before storage occurs.

The validation happens before calling the parent’s setattr, preventing invalid states. This pattern is useful for domain models.

Read-Only Attributes

setattr can make certain attributes read-only by preventing their modification after initial assignment.

readonly.py

class Constants: def init(self): super().setattr("_values", {}) self._values[“PI”] = 3.14159

def __setattr__(self, name, value):
    if name in self._values:
        raise AttributeError(f"Cannot modify {name}")
    super().__setattr__(name, value)

const = Constants() print(const._values[“PI”]) # 3.14159

const.PI = 3.14 # Raises AttributeError

const.E = 2.71828 # Allowed

This constants class stores protected values in a dictionary and prevents their modification. New attributes can still be added normally.

The trick is using super().setattr in init to bypass the custom setattr for initial setup.

Dynamic Attribute Creation

setattr can dynamically transform attribute values or create derived attributes when assignments occur.

dynamic.py

class CaseInsensitiveDict: def setattr(self, name, value): lower_name = name.lower() if lower_name == “data”: super().setattr(“data”, {}) else: self.data[lower_name] = value

def __getattr__(self, name):
    return self.data.get(name.lower())

d = CaseInsensitiveDict() d.Color = “blue” d.COLOR = “red” print(d.color) # “red” (last assignment wins)

This dictionary-like object stores attributes case-insensitively. All assignments go into a data dictionary using lowercase keys.

Note the special handling for the ‘data’ attribute itself, which needs to be stored normally to avoid infinite recursion.

Preventing Attribute Creation

setattr can completely prevent new attribute creation, making objects strictly follow a predefined schema.

prevent.py

class StrictPerson: slots = (“name”, “age”)

def __setattr__(self, name, value):
    if name not in self.__slots__:
        raise AttributeError(f"Cannot add attribute {name}")
    super().__setattr__(name, value)

p = StrictPerson() p.name = “Bob” # Allowed p.age = 40 # Allowed

p.email = “bob@example.com” # Raises AttributeError

This class combines slots with setattr to create a strictly controlled object. Only predefined attributes can be set.

The slots declaration provides memory efficiency while setattr enforces the schema at runtime.

Best Practices

  • Always call super().setattr: Unless intentionally blocking storage

  • Avoid infinite recursion: Use super() or direct dict access carefully

  • Document behavior: Clearly document any special assignment logic

  • Consider performance: setattr adds overhead to all assignments

  • Use @property when possible: For simple cases, properties may be cleaner

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.

ad ad