# 153. Implementing Custom Iterators

Here are 10 Python snippets demonstrating how to implement custom iterators to control iteration behavior. Each snippet is separated by a delimiter.

***

**Snippet 1: Basic Custom Iterator**

```python
class Counter:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        self.current += 1
        return self.current - 1

# Usage
for number in Counter(1, 5):
    print(number)  # Output: 1, 2, 3, 4
```

***

**Snippet 2: Iterator for Fibonacci Sequence**

```python
class Fibonacci:
    def __init__(self, max_value):
        self.a, self.b = 0, 1
        self.max_value = max_value

    def __iter__(self):
        return self

    def __next__(self):
        if self.a > self.max_value:
            raise StopIteration
        self.a, self.b = self.b, self.a + self.b
        return self.a - self.b

# Usage
for num in Fibonacci(10):
    print(num)  # Output: 0, 1, 1, 2, 3, 5, 8
```

***

**Snippet 3: Reverse Iterator**

```python
class Reverse:
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]

# Usage
for char in Reverse("Python"):
    print(char)  # Output: n o h t y P
```

***

**Snippet 4: Custom Range Iterator**

```python
class CustomRange:
    def __init__(self, start, stop, step):
        self.current = start
        self.stop = stop
        self.step = step

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.stop:
            raise StopIteration
        value = self.current
        self.current += self.step
        return value

# Usage
for num in CustomRange(1, 10, 2):
    print(num)  # Output: 1, 3, 5, 7, 9
```

***

**Snippet 5: Circular Iterator**

```python
class CircularIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if not self.data:
            raise StopIteration
        value = self.data[self.index]
        self.index = (self.index + 1) % len(self.data)
        return value

# Usage
circular = CircularIterator([1, 2, 3])
for _ in range(10):
    print(next(circular), end=" ")  # Output: 1 2 3 1 2 3 1 2 3 1
```

***

**Snippet 6: File Line Iterator**

```python
class FileLineIterator:
    def __init__(self, file_name):
        self.file = open(file_name, 'r')

    def __iter__(self):
        return self

    def __next__(self):
        line = self.file.readline()
        if not line:
            self.file.close()
            raise StopIteration
        return line.strip()

# Usage
for line in FileLineIterator('example.txt'):
    print(line)
```

***

**Snippet 7: Infinite Iterator**

```python
class InfiniteCounter:
    def __init__(self, start=0):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        self.current += 1
        return self.current - 1

# Usage
counter = InfiniteCounter()
for _ in range(5):
    print(next(counter))  # Output: 0, 1, 2, 3, 4
```

***

**Snippet 8: Skipping Iterator**

```python
class SkipIterator:
    def __init__(self, data, skip):
        self.data = data
        self.index = 0
        self.skip = skip

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        value = self.data[self.index]
        self.index += self.skip
        return value

# Usage
for num in SkipIterator([1, 2, 3, 4, 5, 6], 2):
    print(num)  # Output: 1, 3, 5
```

***

**Snippet 9: Countdown Iterator**

```python
class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value

# Usage
for num in Countdown(5):
    print(num)  # Output: 5, 4, 3, 2, 1, 0
```

***

**Snippet 10: Batched Iterator**

```python
class BatchIterator:
    def __init__(self, data, batch_size):
        self.data = data
        self.batch_size = batch_size
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        batch = self.data[self.index:self.index + self.batch_size]
        self.index += self.batch_size
        return batch

# Usage
for batch in BatchIterator([1, 2, 3, 4, 5, 6, 7], 3):
    print(batch)  # Output: [1, 2, 3], [4, 5, 6], [7]
```

***
