0

I added an generator function to be able to iterate through a predefined BinaryTree class, but when I run it with the testing code I get a TypeError saying that the BinaryTree class isn't iterable and I'm not sure why. Here is the provided code along with the generator function to traverse the tree as well as the testing code:

# Implement generator function to make class iterable

class BinaryTree:
    def __init__(self, content, leftSubtree=None, rightSubtree=None):
        self.content = content
        self.leftSubtree = leftSubtree
        self.rightSubtree = rightSubtree

    def __repr__(self):
        return str(self.content)

    def traverse(self):  # Generator implementation
        if self.rightSubtree:
            for leaf in self.rightSubtree:
                yield leaf
        yield self.content
        if self.leftSubtree:
            for leaf in self.leftSubtree:
                yield leaf

# Testing code

Node = BinaryTree

if __name__ == '__main__':
    s = Node(1) # Binary tree with 1 Node
    t = Node(10, s, None)   # Binary tree with 10 as content, 1 as left subtree
    tree = BinaryTree(20, None, t)  # Binary tree with 20 as content, no left subtree and t as right subtree

for x in (s, t, tree):
    print(x)

for node in tree:
    print(node)

Here is the error that gets printed out afterwards:

1
Traceback (most recent call last):
10
20
  File "...binaryTree_adt.py", line 34, in <module>
for node in tree:
TypeError: 'BinaryTree' object is not iterable

Process finished with exit code 1

I know that for generators you don't need to create an iterator class as the generator will iterate through the items in a similar fashion. When I change the name of the "traverse" method to iter I don't get any errors, but when the name is changed I do.

1 Answer 1

1

BinaryTree itself is not iterable, no. Only the result produced by the BinaryTree().traverse() method is:

for node in tree.traverse():

However, you'd need to adjust the method itself to use the .traverse() methods of the child nodes too:

def traverse(self):  # Generator implementation
    if self.rightSubtree:
        for leaf in self.rightSubtree.traverse():
            yield leaf
    yield self.content
    if self.leftSubtree:
        for leaf in self.leftSubtree.traverse():
            yield leaf

If you want to make an instance itself iterable, you need to implement a __iter__ method that either returns an iterator (a generator function produces this, for example) or returns __self__ at which point you also need to provide next or __next__ method, depending on your Python version.

Here, renaming traverse to __iter__ suffices:

class BinaryTree:
    def __init__(self, content, leftSubtree=None, rightSubtree=None):
        self.content = content
        self.leftSubtree = leftSubtree
        self.rightSubtree = rightSubtree

    def __repr__(self):
        return str(self.content)

    def __iter__(self):
        if self.rightSubtree:
            for leaf in self.rightSubtree:
                yield leaf
        yield self.content
        if self.leftSubtree:
            for leaf in self.leftSubtree:
                yield leaf

Demo:

>>> class BinaryTree:
...     def __init__(self, content, leftSubtree=None, rightSubtree=None):
...         self.content = content
...         self.leftSubtree = leftSubtree
...         self.rightSubtree = rightSubtree
...     def __repr__(self):
...         return str(self.content)
...     def __iter__(self):
...         if self.rightSubtree:
...             for leaf in self.rightSubtree:
...                 yield leaf
...         yield self.content
...         if self.leftSubtree:
...             for leaf in self.leftSubtree:
...                 yield leaf
...
>>> Node = BinaryTree
>>> s = Node(1)
>>> t = Node(10, s, None)
>>> tree = BinaryTree(20, None, t)
>>> for x in (s, t, tree):
...     print(x)
...
1
10
20
>>> for node in tree:
...     print(node)
...
10
1
20
Sign up to request clarification or add additional context in comments.

4 Comments

When I originally use the name iter the code produced the correct output. I'm just confused as to why it would need to be named as such even though a generator (yield) is being used?
@BrxttB: a generator is a method to produce an iterable; the result of calling traversable() is that iterable. You can make an instance iterable without using yield; just return self from __iter__ and have __next__ produce a value each time it is called.
@BrxttB: see How to make class iterable? for an example of a class that's iterable without using yield.
I know how to make it iterable using iter, it's just that in this particular case I need to use a generator so I was under the impression that with a generator you don't need to define an iter method. Just the method that uses "yield"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.