AttributeError: can't set attribute in Python [Solved]

avatar
Borislav Hadzhiev

Last updated: Apr 11, 2024
4 min

banner

# Table of Contents

  1. AttributeError: can't set attribute in Python
  2. Using the _replace() method to solve the error
  3. Solving the error when using the @property decorator
  4. Using a dictionary instead of a named tuple
  5. Using a class instead of a named tuple

# AttributeError: can't set attribute in Python [Solved]

The Python "AttributeError: can't set attribute" occurs when you try to set an attribute on a named tuple object.

Named tuple objects are immutable, so you have to create a new tuple with the given attribute replaced to solve the error.

NOTE: if you got the error when using the @property decorator in a class, click on the following subheading:

Here is an example of how the error occurs.

main.py
from collections import namedtuple EmployeeRecord = namedtuple( 'EmployeeRecord', ['name', 'age', 'salary'] ) emp1 = EmployeeRecord('Bobby Hadz', 30, 1500) # ๐Ÿ‘‡๏ธ EmployeeRecord(name='Bobby Hadz', age=30, salary=1500) print(emp1) # โ›”๏ธ AttributeError: can't set attribute emp1.salary = 2000

attribute error cant set attribute

Tuples are very similar to lists, but implement fewer built-in methods and are immutable (cannot be changed).

Named tuples are tuples that assign meaning to each position and are also immutable.

We created the named tuple and supplied values for the name, age and salary fields.

We then tried to update the value of the salary attribute and got the error.

# Using the _replace() method to solve the error

One way to solve the error is to use the _replace() method to replace the value of the given attribute.

main.py
from collections import namedtuple EmployeeRecord = namedtuple( 'EmployeeRecord', ['name', 'age', 'salary'] ) emp1 = EmployeeRecord('Bobby Hadz', 30, 1500) # ๐Ÿ‘‡๏ธ EmployeeRecord(name='Bobby Hadz', age=30, salary=1500) print(emp1) emp1 = emp1._replace(salary=2000) print(emp1) print(emp1[2]) # ๐Ÿ‘‰๏ธ 2000 print(emp1.salary) # ๐Ÿ‘‰๏ธ 2000

using replace method to solve the error

The code for this article is available on GitHub

Even though the _replace() method starts with an underscore, it is not private and can be used safely.

The namedtuple._replace method returns a new, named tuple with the updated attribute values.

You can pass as many keyword arguments to the method as necessary.

main.py
from collections import namedtuple EmployeeRecord = namedtuple( 'EmployeeRecord', ['name', 'age', 'salary'] ) emp1 = EmployeeRecord('Bobby Hadz', 30, 1500) # ๐Ÿ‘‡๏ธ EmployeeRecord(name='Bobby Hadz', age=30, salary=1500) print(emp1) emp1 = emp1._replace(age=31, salary=2000) print(emp1) print(emp1[2]) # ๐Ÿ‘‰๏ธ 2000 print(emp1.salary) # ๐Ÿ‘‰๏ธ 2000 print(emp1[1]) # ๐Ÿ‘‰๏ธ 31 print(emp1.age) # ๐Ÿ‘‰๏ธ 31

passing multiple keyword arguments to replace

The code for this article is available on GitHub
Tuples (and named tuples) are immutable, so the only way to update an attribute in a named tuple is to create a new named tuple object with the updated attribute values.

Some of the methods on the named tuple class have a leading underscore but are not considered private and can be used safely.

# Solving the error when using the @property decorator

You might also get the error when using the @property decorator in a class.

Here is an example.

main.py
class Employee: def __init__(self): self._salary = 1500 # โ›”๏ธ AttributeError: can't set attribute 'salary' self.salary = 2000 @property def salary(self): return self._salary emp1 = Employee()
The code for this article is available on GitHub

The error is caused because we tried to set the value of a decorated with @property attribute without defining a setter method.

You can define a setter method in the class to solve the error.

main.py
class Employee: def __init__(self): self._salary = 1500 self.salary = 2000 @property def salary(self): return self._salary @salary.setter def salary(self, new_salary): self._salary = new_salary emp1 = Employee() print(emp1.salary) # ๐Ÿ‘‰๏ธ 2000 emp1.salary = 3000 print(emp1.salary) # ๐Ÿ‘‰๏ธ 3000

passing multiple keyword arguments to replace

The code for this article is available on GitHub

We defined a setter for the salary attribute, so everything works as expected.

We are no longer trying to set the value of an attribute (salary) in the class that has no setter.

# Using a dictionary instead of a named tuple

One way to get around the limitation of named tuples being immutable is to use a dictionary.

main.py
employee = { 'name': 'Bobby Hadz', 'age': 30, 'salary': 1500 } employee['salary'] = 2000 # ๐Ÿ‘‡๏ธ {'name': 'Bobby Hadz', 'age': 30, 'salary': 2000} print(employee) # ๐Ÿ‘‡๏ธ 2000 print(employee['salary'])

using a dictionary instead of a named tuple

The code for this article is available on GitHub

A dictionary is a mapping of key-value pairs.

You can use bracket notation to update the value of a specific key.

We updated the value of the salary key in the example, setting it to 2000.

If you need to access the dictionary by index, convert the dictionary's keys or values to a list.

main.py
employee = { 'name': 'Bobby Hadz', 'age': 30, 'salary': 1500 } print(list(employee.keys())[0]) # ๐Ÿ‘‰๏ธ name print(list(employee.values())[0]) # ๐Ÿ‘‰๏ธ Bobby Hadz # ๐Ÿ‘‡๏ธ ('name', 'Bobby Hadz') print(list(employee.items())[0])

Starting with Python version 3.7, the insertion order for key-value pairs is preserved.

The dict.keys() method returns a new view of the dictionary's keys.

main.py
employee = { 'name': 'Bobby Hadz', 'age': 30, 'salary': 1500 } # ๐Ÿ‘‡๏ธ ['name', 'age', 'salary'] print(list(employee.keys())) # ๐Ÿ‘‡๏ธ ['Bobby Hadz', 30, 1500] print(list(employee.values())) # ๐Ÿ‘‡๏ธ [('name', 'Bobby Hadz'), ('age', 30), ('salary', 1500)] print(list(employee.items()))

The dict.values() method returns a new view of the dictionary's values.

The dict.items() method returns a new view of the dictionary's items ((key, value) pairs).

If you need to use dot "." notation to access the keys in a dictionary, check out the following article.

# Using a class instead of a named tuple

Alternatively, you can use a class instead of a named tuple.

main.py
class Employee(): def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary employee_1 = Employee('Bobby Hadz', 30, 1500) employee_1.salary = 2000 employee_1.age = 31 print(employee_1.salary) # ๐Ÿ‘‰๏ธ 2000 print(employee_1.age) # ๐Ÿ‘‰๏ธ 31

using class instead of named tuple

The code for this article is available on GitHub

The __init__ method gets called when the class is instantiated.

The class in the example takes the name, age and salary arguments.

Once we create the class instance, we can use dot notation to update or read the attributes of the class.

Class instances are not immutable, so updating the value of an attribute is as simple as instance.attr = 'new value'.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev