1

I am trying to understand the behavior of iterators in Python, particularly when using the copy.copy() and copy.deepcopy() functions. I have the following script:

import copy

my_list = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]

def iterate_5_elements(iterator):
    for _ in range(5):
        print(next(iterator))

my_iterator1 = enumerate(my_list)
iterate_5_elements(my_iterator1)
iterate_5_elements(my_iterator1)
print("--------------------------------")

my_iterator2 = enumerate(my_list)
iterate_5_elements(copy.copy(my_iterator2))
iterate_5_elements(copy.copy(my_iterator2))
print("--------------------------------")

my_iterator3 = enumerate(my_list)
iterate_5_elements(copy.deepcopy(my_iterator3))
iterate_5_elements(copy.deepcopy(my_iterator3))
print("--------------------------------")

And it produces this output:

(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(5, 'f')
(6, 'g')
(7, 'h')
(8, 'i')
(9, 'j')
--------------------------------
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(0, 'f')
(1, 'g')
(2, 'h')
(3, 'i')
(4, 'j')
--------------------------------
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
--------------------------------

The first and third iterator behave as expected. However, with the second iterator (using copy.copy()), the output is strange: The letters continue from 'f' instead of starting from 'a' (expected behavior, its a shallow copy) but the numbers reset. Why does this happen? Why does the alphabet continues from 'f' after the first iteration, even though the index seems to reset?

I was expecting the shallow copy to behave exactly like the first iterator, I.E. printing 0 through 9 and 'a' through 'f'

0

1 Answer 1

3

Maybe it makes it clear with a custom my_enumerate class:

import copy

my_list = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]

class my_enumerate:
    def __init__(self, container):
        self.counter = 0
        self.iterator = iter(container)

    def __next__(self):
        self.counter += 1
        return self.counter - 1, next(self.iterator)

def iterate_5_elements(iterator):
    for _ in range(5):
        print(next(iterator))

my_iterator1 = my_enumerate(my_list)
iterate_5_elements(my_iterator1)
iterate_5_elements(my_iterator1)
print("--------------------------------")

# Each copy has its own counter but a reference to the same iterator.
# Both shallow copies use the same iterator.
my_iterator2 = my_enumerate(my_list)
my_iterator2_copy1 = copy.copy(my_iterator2)
my_iterator2_copy2 = copy.copy(my_iterator2)
iterate_5_elements(my_iterator2_copy1)
iterate_5_elements(my_iterator2_copy2)
print("Same inner iterator:", my_iterator2_copy1.iterator is my_iterator2_copy2.iterator)
print("--------------------------------")

# Each copy has its own counter and a reference to a seperate iterator.
# Both deep copies use seperate iterators.
my_iterator3 = my_enumerate(my_list)
my_iterator3_copy1 = copy.deepcopy(my_iterator3)
my_iterator3_copy2 = copy.deepcopy(my_iterator3)
iterate_5_elements(my_iterator3_copy1)
iterate_5_elements(my_iterator3_copy2)
print("Same inner iterator:", my_iterator3_copy1.iterator is my_iterator3_copy2.iterator)
print("--------------------------------")

Output:

(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(5, 'f')
(6, 'g')
(7, 'h')
(8, 'i')
(9, 'j')
--------------------------------
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(0, 'f')
(1, 'g')
(2, 'h')
(3, 'i')
(4, 'j')
Same inner iterator: True
--------------------------------
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
(0, 'a')
(1, 'b')
(2, 'c')
(3, 'd')
(4, 'e')
Same inner iterator: False
--------------------------------

In the first case, there is one iterator that is iterated twice.

In the third case, there a two seperate iterators created with a deep clone.

In the interesting second case, the counter and the iterator inside the instance are copied. That means, that both shallow copies of the iterator have seperate counters, but they use the same iterator for the container.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.