You are reading solutions
alfie-grace-headshot-square2.jpg
Author: Alfie Grace
Data Scientist

Python AttributeError: 'tuple' object has no attribute

AttributeError: 'tuple' object has no attribute

This error occurs when attempting to access the values of a tuple incorrectly.

Functions that return multiple variables will output the results in a tuple, so we cannot use dot-access to retrieve the values.

Example 1

For example, let's create a simple function that returns two values:

def create_tuple():
    val_1 = 5
    val_2 = 10
    return val_1, val_2

If we call the function and attempt to access the tuple's elements with dot-access, i.e. as an attribute, we will see the error:

val_1 = create_tuple().val_1
Out:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-2f0480fa9e27> in <module>
----> 1 val_1 = create_tuple().val_1

AttributeError: 'tuple' object has no attribute 'val_1'

As we can see from the error message, the attribute val_1 does not exists for the tuple.

What's an attribute?

What does the error mean by attribute? An example of an attribute would be class variables. For example, say we have a simple Tree class:

class Tree:
    def __init__(self, kind):
        self.kind = kind  # kind is an attribute
        
new_tree = Tree('pine')

print(new_tree.kind)  # kind is dot-accessible
Out:
pine

We can see in this example that kind is an attribute of the Tree class, and we can access it with a dot.

In contrast, to correctly access the tuple's value, we need to use an index:

result = create_tuple()
val_1 = result[0]
val_2 = result[1]

print(val_1, val_2)
Out:
5 10

Or, alternatively, by using tuple unpacking:

val_1, val_2 = create_tuple()
print(val_1, val_2)
Out:
5 10

Often this error occurs when using an external library where you didn't know a function returned a tuple. We'll describe examples of this below.

If you create your own function and want your return value to have dot-access, you can use a namedtuple or a class.

Cause 1: Treating Tuple Values as Named Attributes

As mentioned previously, any function which returns multiple values will output them as a tuple-type by default, which cannot be dot-accessed.

To demonstrate this, let's write a small function called perform_calculations that takes two numbers and outputs values for their sum, product, and quotient:

def perform_calculations(a, b):
    add = a + b
    mult = a * b
    div = a / b
    return add, mult, div

perform_calculations(4, 2)
Out:
(6, 8, 2.0)

Python packs function outputs into a tuple, as shown by the parentheses containing the sequence. We know that one of the function's outputs is the product of the two numbers, so let's try and access that:

results = perform_calculations(4, 2)

print(results.mult)
Out:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-17-475c1deeb55f> in <module>
      1 results = perform_calculations(4, 2)
      2 
----> 3 print(results.mult)

AttributeError: 'tuple' object has no attribute 'mult'

Our results variable is a tuple, so we can't access specific values in the manner shown above.

Solution

One way of assigning tuple values to variables is to unpack the tuple:

results = perform_calculations(4, 2)
add, mult, div = results

print(mult)
Out:
8

The solution shows that tuple values need to be unpacked and assigned to variables before directly accessing them. Note that we can unpack our function output straight into add, mult, and div, without having to use results as a go-between:

add, mult, div = perform_calculations(5, 7)

print(add)
Out:
12

This article contains a few more examples of unpacking functions and lists.

We could also use indices to reference specific tuple values:

mult = perform_calculations(5, 7)[1]

print(mult)
Out:
35

Optionally, if you would like to have dot-access to elements, you can use a namedtuple like so:

from collections import namedtuple

Calculations = namedtuple('Calculations', ['add', 'mult', 'div'])

def perform_calculations(a, b):
    c = Calculations(
        add=a + b,
        mult=a * b,
        div=a / b
    )
    return c

r = perform_calculations(5, 7)

print(r.mult)
Out:
35

Similarly, this could be accomplished by using a class.

We've been using a function we created in these examples, but this error often shows up when using third-party libraries, like in the following section.

Cause 2: Not Unpacking iterrows()

This error is seen frequently while using the pandas iterrows() DataFrame method.

Let's take another look at the Iris dataset used in the introduction, iterating through the first three rows of the dataframe using iterrows().

Initially, you might assume iterrows() would return a dot-accessible DataFrame row. Let's try:

# import the dataset and limit to three rows
df = sns.load_dataset('iris')[:3] 

for row in df.iterrows(): 
    print(row.sepal_length)
Out:
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-27-e52734937d9f> in <module>
      3 
      4 for row in df.iterrows():
----> 5     print(row.sepal_length)

AttributeError: 'tuple' object has no attribute 'sepal_length'

We can see row is a tuple, so let's print the rows to examine how to unpack them:

for row in df.iterrows(): 
    print(row, '\n')
Out:
(0, sepal_length       5.1
sepal_width        3.5
petal_length       1.4
petal_width        0.2
species         setosa
Name: 0, dtype: object) 

(1, sepal_length       4.9
sepal_width          3
petal_length       1.4
petal_width        0.2
species         setosa
Name: 1, dtype: object) 

(2, sepal_length       4.7
sepal_width        3.2
petal_length       1.3
petal_width        0.2
species         setosa
Name: 2, dtype: object)

We can see iterrows() generates a tuple that looks like (index, row contents).

Solution

As a result, trying to access specific row features without unpacking them first will throw an error. We need to unpack each row like so:

for i, row in df.iterrows():
    print(f'index: {i}, sepal_length: {row.sepal_length}')
Out:
index: 0, sepal_length: 5.1
index: 1, sepal_length: 4.9
index: 2, sepal_length: 4.7

Alternatively, you could specify that you're only interested in the second value of the tuple using indices, which would prevent you from needing to unpack the tuple.

For example, we could index the iterrows() output as follows:

for row_tuple in df.iterrows():
    # [1] is used to select the second value in the tuple
    print(row_tuple[1].sepal_length)
Out:
5.1
4.9
4.7

The two solutions effectively do the same thing, but developers use the first approach more commonly as it is slightly more readable. Furthermore, you can throw away the index with an underscore if you don't need it:

for _, row in df.iterrows():
    print(row.sepal_length)
Out:
5.1
4.9
4.7

The underscore conveys to readers of your code that you intend not to use the index value.

Summary

The error AttributeError: 'tuple' object has no attribute is caused when treating the values within a tuple as named attributes. The error also occurs very frequently when using iterrows(), hence the big focus on avoiding the error in that case.

It's important to remember that any functions returning multiple values will automatically store the outputs in a tuple, which could also potentially cause this error to come up. Luckily, the error is pretty easy to avoid in most cases by unpacking the tuple first, assigning the values to variables. Alternatively, using indices to index a tuple is also a viable solution.

Take the internet's best data science courses Learn More

Meet the Authors

alfie-grace-headshot-square2.jpg

Alfie graduated with a Master's degree in Mechanical Engineering from University College London. He's currently working as a top-rated data scientist on Upwork. Find him on LinkedIn.

Brendan Martin
Editor: Brendan Martin
Founder of LearnDataSci

Get updates in your inbox

Join over 7,500 data science learners.