Iterator va generator
Tags: python, programming, uz
Published: 2023-01-25 03:36:14

## Iterator
Python da iterator obyektlar `__iter__` va `__next__` metodlari orqali amalga oshiriladi. Iterator obyektni iterable tiplar list, tuple, dict, str va boshqa, `iter()` built_in metodi orqali yaratamiz va elementlarni olishda `next()` metodidan foydalanamiz.
![iterator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sy1hyc984waknbl3t1ef.jpg)
Manba: [Codingninjas](https://files.codingninjas.in/article_images/iterators-and-generators-in-python-3-1657095549.jpg)

```python
my_list = [2, 4, 8, 16, 32]

iterator = iter(my_list)

print(next(iterator))  # 2
print(next(iterator))  # 4
print(next(iterator))  # 8
print(next(iterator))  # 16
print(next(iterator))  # 32
print(next(iterator))  # StopIteration
```
Iterator oxirgi elementidan keyin `next` ishlatilsa `StopIteration` xatolik qaytaradi.

Xuddi shuni for loop orqali amalga oshiramiz.

```python
my_list = [2, 4, 8, 16, 32]

for item in my_list:
    print(item)

# 2
# 4
# 8
# 16
# 32
```
Iterator obyekt elementlarini for loop orqali ham olishimiz mumkin.

```python
my_list = [2, 4, 8, 16, 32]

iterator = iter(my_list)

for item in iterator:
    print(item)

# 2
# 4
# 8
# 16
# 32
```
`__iter__` va `__next__` metodlarni o'zgartirgan holda iterator obyekt yaratamiz.
```python
class PowTwo:
    def __init__(self, limit=0):
        self.limit = limit

    def __iter__(self):
        self.n = 1
        return self

    def __next__(self):
        if self.n <= self.limit:
            pow_number = 2 ** self.n
            self.n += 1
            return pow_number
        else:
            raise StopIteration


numbers = PowTwo(5)
iterator = iter(numbers)

print(next(iterator))  # 2
print(next(iterator))  # 4
print(next(iterator))  # 8
print(next(iterator))  # 16
print(next(iterator))  # 32
print(next(iterator))  # StopIteration
```
`iter()` va `next()` built_in metodlar ishlatganimizda class da yozilgan `__iter__` va `__next__` metodlarga murojaat qilinadi.
```python
numbers = PowTwo(5)
iterator = numbers.__iter__()

print(iterator.__next__())  # 2
print(iterator.__next__())  # 4
print(iterator.__next__())  # 8
print(iterator.__next__())  # 16
print(iterator.__next__())  # 32
print(iterator.__next__())  # StopIteration
```

## Generator
![generator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehks8kqt10132m317j13.jpg)Manba: [Medium](https://medium.com/edonec/basics-of-generator-functions-in-javascript-cbadb01c680a)

Python da generator funksiya oddiy funksiyadek bo'ladi, faqat `yield` kalit so'zi orqali qiymat qaytariladi.
```python
def generator():
    yield 1
    yield 2
    yield 3


for value in generator():
    print(value)
# 1
# 2
# 3
```
Generator funksiya generator obyekt qaytaradi.
```python
print(generator())
# <generator object generator at 0x7f1af486aea0>
```
Generator obyekt dan iterator sifatida foydalanib `next()` orqali ham qiymatlarni olishimiz mumkin.
```python
my_generator = generator()

print(next(my_generator))  # 1
print(next(my_generator))  # 2
print(next(my_generator))  # 3
print(next(my_generator))  # StopIteration
```

## Nima uchun funksiyada `return` o'rniga `yield` ishlatilyapti?
`return` funksiyada berilgan qiymatni qaytaradi, `yield` esa ketma-ketlikni qaytarib yuboradi. Biz funksiyada butun bir ketma-ketlik xotirada saqlamasdan qaytarmoqchi bo'lsak `yield` foydalanishimiz kerak.

Raqamlar ro'yxatidan ularning kvadratlarini qaytaradigan generator funksiya yaratib ko'ramiz.
```python
def square(nums):
    for num in nums:
        yield num ** 2


numbers = [1, 2, 3]
generator = square(numbers)

print(next(generator))  # 1
print(next(generator))  # 4
print(next(generator))  # 9
print(next(generator))  # StopIteration
```
Generator funksiya orqali qaytariladigan ketma-ketlikni `sum()` va boshqa iterable qabul qiladigan funksiyalar orqali ishlatsak ham bo'ladi.
```python
numbers = [1, 2, 3]
sum_of_square_numbers = sum(square(numbers))
print(sum_of_square_numbers)  # 14
```