[Solved] AttributeError: can’t set attribute in python

4/5 - (1 vote)

Problem Formulation

You create a namedtuple object in your code and you want to overwrite one of the attribute values like you’re used to for normal objects:

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche.speed = 400

But, unexpectedly, Python raises an AttributeError: can't set attribute. ?

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\code.py", line 5, in <module>
    porsche.speed = 400
AttributeError: can't set attribute

What can you do? ? Let’s understand the reason this error occurs—after that, you’ll learn the best fixes.

Why Does This Error Occur?

Any namedtuple object is also a tuple (subclass relationship), so it is immutable — you cannot change it after creation.

For example, you cannot change a tuple ('Alice', 'Bob') to ('Ann', 'Bob') without creating a new one.

If you attempt to change a namedtuple object’s attribute value, you try to modify an immutable object. Consequently, Python raises the AttributeError: can't set attribute.

Fix #1: Use the namedtuple._replace() Method

The easiest way to fix the AttributeError:can't set attribute is to create a new namedtuple object with the namedtuple._replace() method. The result is a new namedtuple object with all attributes copied except the newly passed one.

For example, if you want to change attribute x of named tuple object n to the value 42, call n._replace(x=42).

Here’s the fix applied to our previous example:

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche = porsche._replace(speed=400)

You can see in the following print() statement that the original attribute value for speed has changed from 300 to 400.

# Car(speed=300, color='gold')

Note that this is generally inefficient because all other attributes have to be copied just to update a single attribute!

Can we resolve this? Sure!

Fix #2: Don’t Use Namedtuples But Classes

Namedtuples are immutable objects, so you cannot change the attribute values. But standard Python classes are mutable, so they can be changed arbitrarily. Specifically, you can change an existing attribute’s value and even dynamically add a new attribute to an existing object.

The following code accomplishes the same thing as our example code—but it does so more effectively using standard Python classes rather than namedtuples:

class Car:
    def __init__(self, speed, color):
        self.speed = speed
        self.color = color

porsche = Car(300, 'gold')
porsche.speed = 400

Let’s confirm that the attribute value has changed to 400:

# 400

You can even add an additional attribute, say price, to the object like so:

porsche.price = 100000
# 100000

Fix #3: Use Dictionaries

For lightweight applications, you can also use a simple dictionary instead of namedtuples or classes. Dictionaries are designed to hold key: value instead of attribute: value pairs. For instance, you can create a dictionary {'key_1': 'value_1', 'key_2': 'value_2'} to store two keys instead of attributes. Instead of updating an attribute, you’d now update a key using dict['key_1'] = 'new_value'.

Here’s this solution applied to our previous problem—quite some simplification has happened!

porsche = {'speed': 300, 'color': 'gold'}
porsche['speed'] = 400

Let’s check if the attribute has changed:

# 400

Indeed, the output has changed and no error message exists.

Fix #4: If You Must Use Namedtuples, Use A Mutable Attribute

If you must use namedtuples — for example to update an existing attribute with a new value — and you cannot create a new one with _replace() for efficiency reasons, do the following:

Create the nametuple object’s attributes as mutable objects such as lists. You can always change the contents of a list because it is mutable. So, semantically, you modify the value of a namedtuple attribute without actually violating the immutability criterion—the namedtuple still points to the same mutable object (e.g., a list) in memory.

Here’s this solution applied to the example problem:

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car([300], 'gold')
porsche.speed[0] = 400

Although you may not find this solution particularly sexy, it works like a charm:

# [400]

The original value for speed has changed and no error occurred.

Where to Go From Here?

Enough theory. Letโ€™s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. Thatโ€™s how you polish the skills you really need in practice. After all, whatโ€™s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

๐Ÿš€ If your answer is YES!, consider becoming a Python freelance developer! Itโ€™s the best way of approaching the task of improving your Python skillsโ€”even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar โ€œHow to Build Your High-Income Skill Pythonโ€ and learn how I grew my coding business online and how you can, tooโ€”from the comfort of your own home.

Join the free webinar now!