Skip to content

How to Resolve "AttributeError: can't set attribute" in Python

Fix the Python AttributeError when trying to modify immutable namedtuple objects or read-only properties by using _replace() or defining setter methods.

The AttributeError: can't set attribute error in Python occurs when you try to assign a value to an attribute that is read-only or on an object that doesn't allow attribute modification. This commonly happens with namedtuple instances (which are immutable) and with properties defined using the @property decorator without a corresponding setter method.

This guide explains the causes of this error and provides the correct solutions.

AttributeError with namedtuple

namedtuple instances (from the collections module) are immutable. You can not change their attributes after creation:

from collections import namedtuple

EmployeeRecord = namedtuple(
    'EmployeeRecord',
    ['name', 'age', 'salary']
)

emp1 = EmployeeRecord('Tom Nolan', 25, 1500)
print(emp1) # Output: EmployeeRecord(name='Tom Nolan', age=25, salary=1500)
  • Why it happens: namedtuple is designed to be a lightweight, immutable data structure, similar to a tuple, but with named fields.

Creating a Modified namedtuple with _replace()

To "modify" a namedtuple, you create a new instance with the changed values using the _replace() method:

from collections import namedtuple

EmployeeRecord = namedtuple(
    'EmployeeRecord',
    ['name', 'age', 'salary']
)
emp1 = EmployeeRecord('Tom Nolan', 25, 1500)
print(emp1) # Output: EmployeeRecord(name='Tom Nolan', age=25, salary=1500)

emp1 = emp1._replace(salary=2000) # Create a NEW named tuple with the changes
print(emp1)         # Output: EmployeeRecord(name='Tom Nolan', age=25, salary=2000)

print(emp1[2])      # Output: 2000 

print(emp1.salary)  # Output: 2000 
  • _replace() creates a new namedtuple instance with the specified fields changed. The original emp1 is not modified.

AttributeError with @property (Read-Only Properties)

The @property decorator creates read-only attributes. If you try to assign to them directly, you'll get the AttributeError.

class Employee:
    def __init__(self):
        self._salary = 1500

    @property
    def salary(self):
        return self._salary

emp1 = Employee()

Adding a Setter Method

To allow setting the value, define a setter method using the @<property_name>.setter decorator:

class Employee:
    def __init__(self):
        self._salary = 1500
        self.salary = 2000 # The setter is used

    @property  # Getter
    def salary(self):
        return self._salary

    @salary.setter # Setter
    def salary(self, new_salary):
        if new_salary >= 0:
            self._salary = new_salary
        else:
            raise ValueError("Salary can not be negative")

emp1 = Employee()
print(emp1.salary)  # Output: 2000 (uses getter)

emp1.salary = 2500  # Uses the setter
print(emp1.salary)  # Output: 2500
  • The @property decorator makes salary appear like a regular attribute, but calls the getter method when accessed.
  • The @salary.setter decorator defines the method that's called when you assign to emp1.salary. This setter can (and should!) include validation logic.

Alternatives to namedtuple (When You Need Mutability)

If you need to modify attributes after creation, namedtuple is the wrong choice. Consider these alternatives:

Using a Standard Dictionary

Dictionaries are mutable, so you can freely change their values:

employee = {
    'name': 'Tom Nolan',
    'age': 25,
    'salary': 1500
}

employee['salary'] = 2000   # Change a value directly.
print(employee)             # Output: {'name': 'Tom Nolan', 'age': 25, 'salary': 2000}
print(employee['salary'])   # Output: 2000

Using a Class

For more complex objects with behavior (methods), define a regular class:

class Employee():
    def __init__(self, name, age,  salary):
        self.name = name
        self.age = age
        self.salary = salary
employee_1 = Employee('Tom Nolan', 25, 1500)

employee_1.salary = 2000  # Update attributes
employee_1.age = 31

print(employee_1.salary)  # Output: 2000
print(employee_1.age)     # Output: 31

Conclusion

The AttributeError: can't set attribute error in Python occurs when trying to modify an immutable object (like a namedtuple) or a read-only property.

  • Understand the immutability of namedtuple and use _replace() to create modified copies.
  • For read-only properties, define a setter method using the @<property_name>.setter decorator.
  • If you need mutability, use a standard dictionary or a regular class instead of a namedtuple.
Navigation