easy +8 pts

Close generator early

Track how many values a generator yields when it's closed manually.

Write a function `track_generator(gen)` that accepts an iterable or iterator and returns a generator-like object that yields the same items as the input, but also keeps track of how many items have been yielded so far. The wrapper must support the `close()` method exactly like a normal generator, and after `close()` is called, it must make its count available via a `yielded_count` attribute on the returned object. Specifically: - `track_generator(gen)` returns an object that is an iterator (has `__iter__` and `__next__`). - Iterating over the returned object yields each item from `gen` one by one. - The returned object has an attribute `yielded_count` that reflects the number of items successfully yielded so far before the generator is closed or exhausted. - When the underlying `gen` is exhausted, `yielded_count` stays at the total number of items yielded. - When the returned object's `close()` method is called (either explicitly or via a `with` statement, or by the garbage collector), the generator stops immediately and `yielded_count` must be set to the number of items yielded before the close. - The `close()` method of the returned object must also call the underlying generator's `close()` if the underlying has one, but must not raise any errors if the underlying is not a generator (e.g., an iterable like a list). - If the input is not iterable and not an iterator, raise `TypeError` with message `'gen must be iterable'`. - The implementation must not use any external libraries or global variables. You may use a custom class that implements the iterator protocol, but the result must behave like a generator (support `next()`, `iter()`, and `close()`). - Calling `close()` on an already-closed or exhausted wrapper should be a no-op and not raise.

Constraints

- Input can be any iterable (list, tuple, string, generator, etc.) or iterator. - The input may produce any number of items, including zero. - Time complexity: O(1) per yielded item. Space complexity: O(1) beyond the input. - The returned object must support the iterator protocol and a `close()` method.

Example

>>> g = track_generator([1, 2, 3])
>>> g.yielded_count
0
>>> next(g)
1
>>> next(g)
2
>>> g.close()
>>> g.yielded_count
2

>>> g = track_generator((x for x in range(4)))
>>> for val in g:
...     print(val)
...     if val == 2:
...         g.close()
0
1
2
>>> g.yielded_count
3
8 points ~10 min

Recent Submissions

No submissions yet — hit Run Tests to try!

Hints

You can wrap the generator inside a custom class that has __iter__, __next__, and close methods.
Keep a counter attribute that increments every time __next__ returns an item.
When close is called, set a flag to stop further iteration and set the counter attribute, but ensure the underlying generator's close is invoked safely.
Python 3
All tests passed!
Test Results
Press Ctrl+Enter or click Run Tests to execute your code.