Python magic methods to get/set attributes

Stanislav Lazarenko
5 min readFeb 27, 2023

--

In Python, you can define special methods called “magic methods” or “dunder methods” to customize how attributes are accessed and modified in your objects. Here are the magic methods that you can use to get and set attributes in Python:

  1. __getattr__(self, name): This method is called when an attempt is made to access an attribute that does not exist in the object's namespace. It takes a single argument name, which is the name of the attribute being accessed. You can use this method to dynamically generate attributes or raise an AttributeError if the attribute does not exist.
  2. __getattribute__(self, name): This method is called when an attempt is made to access any attribute of the object. It takes a single argument name, which is the name of the attribute being accessed. You can use this method to intercept attribute access and customize how the attribute is retrieved.
  3. __setattr__(self, name, value): This method is called when an attempt is made to set the value of an attribute of the object. It takes two arguments name and value, which are the name and value of the attribute being set. You can use this method to intercept attribute setting and customize how the attribute is stored.
  4. __delattr__(self, name): This method is called when an attempt is made to delete an attribute of the object. It takes a single argument name, which is the name of the attribute being deleted. You can use this method to intercept attribute deletion and customize how the attribute is removed.

Here’s an example that demonstrates how to use these magic methods:

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __getattr__(self, name):
if name == "email":
return f"{self.name.lower()}@example.com"
else:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

def __setattr__(self, name, value):
if name == "age":
if value < 0:
raise ValueError("Age must be a positive integer.")
object.__setattr__(self, name, value)

def __delattr__(self, name):
if name == "email":
raise AttributeError("Email attribute cannot be deleted.")
else:
object.__delattr__(self, name)

person = Person("Alice", 30)

# Access the 'name' and 'age' attributes of the person object
print(person.name) # Output: "Alice"
print(person.age) # Output: 30

# Access the 'email' attribute of the person object
print(person.email) # Output: "alice@example.com"

# Attempt to access a non-existing attribute
try:
print(person.address)
except AttributeError as e:
print(str(e)) # Output: "'Person' object has no attribute 'address'"

# Attempt to set the 'age' attribute to a negative value
try:
person.age = -10
except ValueError as e:
print(str(e)) # Output: "Age must be a positive integer."

# Attempt to delete the 'email' attribute
try:
del person.email
except AttributeError as e:
print(str(e)) # Output: "Email attribute cannot be deleted."

In this example, the Person class defines __getattr__, __setattr__, and __delattr__ methods to customize attribute access and modification. The __getattr__ method dynamically generates an email attribute based on the name attribute, and raises an AttributeError if the attribute being accessed does

What is the differences between __getattr__ and __getattribute__

The two magic methods __getattr__ and __getattribute__ in Python are used to customize how attribute access is handled in objects. However, they have some key differences:

  1. __getattr__(self, name): This method is called only when an attempt is made to access an attribute that does not exist in the object's namespace. It takes a single argument name, which is the name of the attribute being accessed. You can use this method to dynamically generate attributes or raise an AttributeError if the attribute does not exist.
  2. __getattribute__(self, name): This method is called for every attribute access on the object, regardless of whether the attribute exists in the object's namespace or not. It takes a single argument name, which is the name of the attribute being accessed. You can use this method to intercept attribute access and customize how the attribute is retrieved.

Here are some key differences between __getattr__ and __getattribute__:

  1. Handling of existing attributes: __getattr__ is only called when an attribute is not found in the object's namespace, whereas __getattribute__ is called for every attribute access. This means that if you define __getattribute__ in a class, it will intercept all attribute accesses, even if the attribute exists in the object's namespace.
  2. Recursion: Since __getattribute__ is called for every attribute access, it can potentially cause infinite recursion if you try to access an attribute within the __getattribute__ method itself. To avoid this, you should use object.__getattribute__(self, name) to retrieve the attribute within the method.
  3. Handling of errors: If __getattr__ raises an AttributeError, the interpreter will fall back to using the default attribute lookup behavior. However, if __getattribute__ raises an AttributeError, the interpreter will raise the exception directly.
  4. Performance: Since __getattribute__ is called for every attribute access, it can be less performant than __getattr__ if you only need to intercept attribute accesses for non-existent attributes.

Here’s an example that demonstrates the difference between __getattr__ and __getattribute__:

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __getattr__(self, name):
if name == "email":
return f"{self.name.lower()}@example.com"
else:
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

def __getattribute__(self, name):
print(f"Getting attribute {name}")
return object.__getattribute__(self, name)

person = Person("Alice", 30)

# Access the 'name' and 'age' attributes of the person object
print(person.name) # Output: "Alice"
print(person.age) # Output: 30

# Access the 'email' attribute of the person object
print(person.email) # Output: "alice@example.com"

# Attempt to access a non-existing attribute
try:
print(person.address)
except AttributeError as e:
print(str(e)) # Output: "'Person' object has no attribute 'address'"

n this example, the Person class defines both __getattr__ and __getattribute__ methods. When the person.name and person.age attributes are accessed, __getattribute__ is called, and when the person.email and person.address attributes are accessed, __getattr__ is called. You can see that __getattribute__ is called for every attribute access, even if the attribute exists in the object's namespace. This is why it is important to use object.__getattribute__(self, name) to retrieve the attribute within the method, as shown in this example.

When the person.email attribute is accessed, __getattr__ is called because the attribute does not exist in the object's namespace. When the person.address attribute is accessed, __getattr__ is also called, and an AttributeError is raised because the attribute does not exist in the object's namespace. Note that the error message is different from the one raised by __getattr__ in this case, because __getattribute__ raises the AttributeError directly.

In general, you should use __getattr__ when you want to customize the behavior of attribute access for non-existent attributes, and use __getattribute__ when you want to intercept all attribute accesses and customize how the attributes are retrieved. However, you should be careful when using __getattribute__ to avoid infinite recursion and performance issues.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response