Skip to main content

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

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)

# ⛔️ AttributeError: can't set attribute
# emp1.salary = 2000 # This line causes the error
  • 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)

# Accessing the value using index
print(emp1[2]) # Output: 2000

# Accessing the value using the named property
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()

# ⛔️ AttributeError: can't set attribute 'salary'
# emp1.salary = 2000 # Trying to set a read-only property

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

# emp1.salary = -100 # This would throw an exception
  • 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.