Last updated: Apr 11, 2024
Reading timeยท4 min
_replace()
method to solve the error@property
decoratorThe 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.
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
Tuples are very similar to lists, but implement fewer built-in methods and are immutable (cannot be changed).
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.
_replace()
method to solve the errorOne way to solve the error is to use the _replace()
method to replace the
value of the given attribute.
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
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.
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
Some of the methods on the named tuple class have a leading underscore but are not considered private and can be used safely.
@property
decoratorYou might also get the error when using the @property
decorator in a class.
Here is an example.
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 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.
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
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.
One way to get around the limitation of named tuples being immutable is to use a dictionary.
employee = { 'name': 'Bobby Hadz', 'age': 30, 'salary': 1500 } employee['salary'] = 2000 # ๐๏ธ {'name': 'Bobby Hadz', 'age': 30, 'salary': 2000} print(employee) # ๐๏ธ 2000 print(employee['salary'])
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.
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.
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.
Alternatively, you can use a class instead of a named tuple.
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
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'
.
You can learn more about the related topics by checking out the following tutorials: