AttributeError: 'tuple' object has no attribute 'first'
Question:
I’m a beginner in python and having trouble with this problem. I wrote this convert_link function while it returned the AttributeError: ‘tuple’ object has no attribute ‘first’. I don’t understand where the tuple came from?
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if link.first is Link.empty:
return []
return [link.first]+convert_link(link.rest)
this the class definition I was given:
class Link:
"""A linked list.
>>> s = Link(1)
>>> s.first
1
>>> s.rest is Link.empty
True
>>> s = Link(2, Link(3, Link(4)))
>>> s.first = 5
>>> s.rest.first = 6
>>> s.rest.rest = Link.empty
>>> s # Displays the contents of repr(s)
Link(5, Link(6))
>>> s.rest = Link(7, Link(Link(8, Link(9))))
>>> s
Link(5, Link(7, Link(Link(8, Link(9)))))
>>> print(s) # Prints str(s)
<5 7 <8 9>>
"""
empty = ()
def __init__(self, first, rest=empty):
assert rest is Link.empty or isinstance(rest, Link)
self.first = first
self.rest = rest
def __repr__(self):
if self.rest is not Link.empty:
rest_repr = ', ' + repr(self.rest)
else:
rest_repr = ''
return 'Link(' + repr(self.first) + rest_repr + ')'
def __str__(self):
string = '<'
while self.rest is not Link.empty:
string += str(self.first) + ' '
self = self.rest
return string + str(self.first) + '>'
This is what I got after running the code in terminal
File "/Users/yiting/Downloads/lab08/lab08.py", line 12, in convert_link
return [link.first]+convert_link(link.rest)
[Previous line repeated 1 more time]
File "/Users/yiting/Downloads/lab08/lab08.py", line 10, in convert_link
if link.first is Link.empty:
AttributeError: 'tuple' object has no attribute 'first'
Answers:
There is no first
or first()
attr in tuple. You need to use indexing like mytuple[0]
.
You test if link.first is Link.empty:
. But Link.empty
is only ever the value for link.rest
(to indicate end of linked list).
You meant to just test if link is Link.empty:
, because when you reached the end of the linked list, you’d recurse calling with Link.empty
itself, not something that has a first
attribute of Link.empty
.
Your function is correct, after that one tweak:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if link is Link.empty: # Removed .first
return []
return [link.first]+convert_link(link.rest)
Your function convert_link
is called recursively, until it reaches the innermost instance of your Link
class that in turn has a link.rest
attribute of type tuple
. This final link.rest
(that contains a tuple
instead of an instance of class Link
) is then passed to convert_link
that then tries to access the attribute .first
that the tuple
does not have.
A quick fix to your function would be to add at its beginning an if
statement, that checks whether the link
being used as input is an instance of the class Link
, and if not converts it into a Link
object:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if not isinstance(link, Link):
link = Link(link)
if link.first == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
Another solution would be to add an additional condition to your if link. First == Link.empty
, checking whether link
has an attribute named first
:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
# The order of both conditions matters here
if not hasattr(link, 'first') or link.first == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
A third solution would be to change the if
statement like so:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
if link == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
print(convert_link(Link.empty))
# Prints:
#
# []
Note
Adding a print
statement right before the second return from your original convert_link
might help better understanding what’s going on inside the convert_link
function. If you add print(link.rest, type(link.rest))
, you would see the following messages being printed to your console:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
# The order of both conditions matters here
if not hasattr(link, 'first') or link.first == Link.empty:
return []
print(link.rest, type(link.rest))
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
"""
<2 3 4> <class '__main__.Link'>
<3 4> <class '__main__.Link'>
<4> <class '__main__.Link'>
() <class 'tuple'> # <---- Here's the problem
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-222-5fde863fdd94> in <module>
14
15
---> 16 convert_link(link)
4 frames
<ipython-input-222-5fde863fdd94> in convert_link(link)
8
9 # The order of both conditions matters here
---> 10 if link.first == Link.empty:
11 return []
12 print(link.rest, type(link.rest))
AttributeError: 'tuple' object has no attribute 'first'
"""
You could also observe the problem using a more direct approach:
print(type(link.rest))
print(type(link.rest.rest))
print(type(link.rest.rest.rest))
print(type(link.rest.rest.rest.rest))
# Prints:
#
# <class '__main__.Link'>
# <class '__main__.Link'>
# <class '__main__.Link'>
# <class 'tuple'>
I’m a beginner in python and having trouble with this problem. I wrote this convert_link function while it returned the AttributeError: ‘tuple’ object has no attribute ‘first’. I don’t understand where the tuple came from?
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if link.first is Link.empty:
return []
return [link.first]+convert_link(link.rest)
this the class definition I was given:
class Link:
"""A linked list.
>>> s = Link(1)
>>> s.first
1
>>> s.rest is Link.empty
True
>>> s = Link(2, Link(3, Link(4)))
>>> s.first = 5
>>> s.rest.first = 6
>>> s.rest.rest = Link.empty
>>> s # Displays the contents of repr(s)
Link(5, Link(6))
>>> s.rest = Link(7, Link(Link(8, Link(9))))
>>> s
Link(5, Link(7, Link(Link(8, Link(9)))))
>>> print(s) # Prints str(s)
<5 7 <8 9>>
"""
empty = ()
def __init__(self, first, rest=empty):
assert rest is Link.empty or isinstance(rest, Link)
self.first = first
self.rest = rest
def __repr__(self):
if self.rest is not Link.empty:
rest_repr = ', ' + repr(self.rest)
else:
rest_repr = ''
return 'Link(' + repr(self.first) + rest_repr + ')'
def __str__(self):
string = '<'
while self.rest is not Link.empty:
string += str(self.first) + ' '
self = self.rest
return string + str(self.first) + '>'
This is what I got after running the code in terminal
File "/Users/yiting/Downloads/lab08/lab08.py", line 12, in convert_link
return [link.first]+convert_link(link.rest)
[Previous line repeated 1 more time]
File "/Users/yiting/Downloads/lab08/lab08.py", line 10, in convert_link
if link.first is Link.empty:
AttributeError: 'tuple' object has no attribute 'first'
There is no first
or first()
attr in tuple. You need to use indexing like mytuple[0]
.
You test if link.first is Link.empty:
. But Link.empty
is only ever the value for link.rest
(to indicate end of linked list).
You meant to just test if link is Link.empty:
, because when you reached the end of the linked list, you’d recurse calling with Link.empty
itself, not something that has a first
attribute of Link.empty
.
Your function is correct, after that one tweak:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if link is Link.empty: # Removed .first
return []
return [link.first]+convert_link(link.rest)
Your function convert_link
is called recursively, until it reaches the innermost instance of your Link
class that in turn has a link.rest
attribute of type tuple
. This final link.rest
(that contains a tuple
instead of an instance of class Link
) is then passed to convert_link
that then tries to access the attribute .first
that the tuple
does not have.
A quick fix to your function would be to add at its beginning an if
statement, that checks whether the link
being used as input is an instance of the class Link
, and if not converts it into a Link
object:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link)
[1, 2, 3, 4]
>>> convert_link(Link.empty)
[]
"""
if not isinstance(link, Link):
link = Link(link)
if link.first == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
Another solution would be to add an additional condition to your if link. First == Link.empty
, checking whether link
has an attribute named first
:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
# The order of both conditions matters here
if not hasattr(link, 'first') or link.first == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
A third solution would be to change the if
statement like so:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
if link == Link.empty:
return []
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
# Prints:
#
# [1, 2, 3, 4]
print(convert_link(Link.empty))
# Prints:
#
# []
Note
Adding a print
statement right before the second return from your original convert_link
might help better understanding what’s going on inside the convert_link
function. If you add print(link.rest, type(link.rest))
, you would see the following messages being printed to your console:
def convert_link(link):
"""Takes a linked list and returns a Python list with the same elements.
>>> link = Link(1, Link(2, Link(3, Link(4))))
>>> convert_link(link) [1, 2, 3, 4]
>>> convert_link(Link.empty) []
"""
# The order of both conditions matters here
if not hasattr(link, 'first') or link.first == Link.empty:
return []
print(link.rest, type(link.rest))
return [link.first] + convert_link(link.rest)
link = Link(1, Link(2, Link(3, Link(4))))
print(convert_link(link))
"""
<2 3 4> <class '__main__.Link'>
<3 4> <class '__main__.Link'>
<4> <class '__main__.Link'>
() <class 'tuple'> # <---- Here's the problem
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-222-5fde863fdd94> in <module>
14
15
---> 16 convert_link(link)
4 frames
<ipython-input-222-5fde863fdd94> in convert_link(link)
8
9 # The order of both conditions matters here
---> 10 if link.first == Link.empty:
11 return []
12 print(link.rest, type(link.rest))
AttributeError: 'tuple' object has no attribute 'first'
"""
You could also observe the problem using a more direct approach:
print(type(link.rest))
print(type(link.rest.rest))
print(type(link.rest.rest.rest))
print(type(link.rest.rest.rest.rest))
# Prints:
#
# <class '__main__.Link'>
# <class '__main__.Link'>
# <class '__main__.Link'>
# <class 'tuple'>