Skip to main content

How to Solve "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.