۱۳

Iterators و Generators 🔄

یاد می‌گیریم چطور داده‌های بزرگ رو بدون مشکل حافظه پردازش کنیم!

Iterator: راهی هوشمند برای پیمایش داده‌ها!

Iterator یه شیء هست که می‌تونه روی مجموعه‌ای از داده‌ها حرکت کنه و هر بار یکی از عناصر رو برگردونه. فکر کن مثل یه نشانگر که روی لیست حرکت می‌کنه و هر بار یه عنصر رو نشون می‌ده. در پایتون، هر چیزی که بشه روش حلقه زد (مثل لیست، تاپل، رشته) یه Iterator محسوب می‌شه!

مثال ساده Iterator
# ساخت یک Iterator از لیست
numbers = [1, 2, 3, 4, 5]
iterator = iter(numbers)

print("استفاده از next() برای دریافت عناصر:")
print(next(iterator))  # خروجی: 1
print(next(iterator))  # خروجی: 2
print(next(iterator))  # خروجی: 3

# استفاده در حلقه for
print("\nاستفاده در حلقه:")
for num in numbers:
    print(f"عدد: {num}")

# ساخت Iterator سفارشی
class CountDown:
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start + 1

# استفاده از Iterator سفارشی
print("\nشمارش معکوس:")
countdown = CountDown(5)
for num in countdown:
    print(f"شمارش: {num}")

Generator: جادوگر تولید داده!

Generator یه نوع خاص از تابع هست که می‌تونه اجراش رو متوقف کنه و بعداً از همون نقطه ادامه بده! با کلمه کلیدی `yield` به جای `return` استفاده می‌کنیم. این کار باعث می‌شه بتونیم داده‌های بزرگ رو بدون اینکه کل حافظه رو پر کنیم، تولید کنیم. فوق‌العاده مفیده برای پردازش فایل‌های بزرگ!

مثال‌های Generator
# Generator ساده برای تولید اعداد
def number_generator(n):
    """تولید اعداد از 0 تا n"""
    for i in range(n):
        print(f"تولید عدد: {i}")
        yield i  # yield به جای return
        print(f"ادامه پس از yield {i}")

# استفاده از Generator
print("=== استفاده از Generator ===")
gen = number_generator(3)
print(f"نوع generator: {type(gen)}")

# دریافت مقادیر یکی یکی
print("\nدریافت مقادیر:")
print(next(gen))  # خروجی: 0
print(next(gen))  # خروجی: 1
print(next(gen))  # خروجی: 2

# Generator برای دنباله فیبوناچی
def fibonacci_generator(limit):
    """تولید دنباله فیبوناچی تا حد مشخص"""
    a, b = 0, 1
    while a < limit:
        yield a
        a, b = b, a + b

print("\n=== دنباله فیبوناچی ===")
for num in fibonacci_generator(100):
    print(num, end=" ")

# Generator Expression (مثل List Comprehension)
print("\n\n=== Generator Expression ===")
squares = (x**2 for x in range(5))
print(f"نوع: {type(squares)}")
for square in squares:
    print(f"مربع: {square}")

Generator های پیشرفته: قدرت واقعی!

Generator ها می‌تونن با هم ترکیب بشن، داده دریافت کنن و حتی exception هم handle کنن! این قابلیت‌ها باعث می‌شه بتونیم pipeline های پردازش داده خیلی قدرتمند بسازیم.

Generator های پیشرفته
# Generator که داده دریافت می‌کنه
def data_processor():
    """پردازشگر داده که می‌تونه داده دریافت کنه"""
    total = 0
    count = 0
    
    while True:
        # دریافت داده از بیرون
        value = yield total / count if count > 0 else 0
        
        if value is None:
            break
            
        total += value
        count += 1
        print(f"دریافت شد: {value}, میانگین فعلی: {total/count:.2f}")

# استفاده از Generator دریافت‌کننده
print("=== Generator دریافت‌کننده ===")
processor = data_processor()
next(processor)  # راه‌اندازی اولیه

# ارسال داده‌ها
processor.send(10)
processor.send(20)
processor.send(30)
result = processor.send(None)  # پایان
print(f"نتیجه نهایی: {result}")

# Generator برای خواندن فایل بزرگ
def read_large_file(file_path, chunk_size=1024):
    """خواندن فایل بزرگ به صورت تکه‌ای"""
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            while True:
                chunk = file.read(chunk_size)
                if not chunk:
                    break
                yield chunk
    except FileNotFoundError:
        yield "فایل پیدا نشد!"

# Pipeline پردازش داده
def filter_numbers(text_generator):
    """فیلتر کردن اعداد از متن"""
    import re
    for text in text_generator:
        numbers = re.findall(r'\d+', text)
        for num in numbers:
            yield int(num)

def square_numbers(number_generator):
    """مربع کردن اعداد"""
    for num in number_generator:
        yield num ** 2

# ترکیب Generator ها
print("\n=== Pipeline پردازش ===")
sample_text = ["سلام 123 دنیا", "عدد 456 و 789", "پایان 999"]
text_gen = (text for text in sample_text)
number_gen = filter_numbers(text_gen)
squared_gen = square_numbers(number_gen)

for squared in squared_gen:
    print(f"مربع: {squared}")

تمرین عملی

یک Generator بنویسید که:

  1. اعداد اول (Prime Numbers) را تولید کند
  2. بتوان حد بالایی برای آن تعیین کرد
  3. هر عدد اول را همراه با شماره‌اش برگرداند
  4. از Iterator pattern استفاده کند
راه حل تمرین
class PrimeGenerator:
    """Generator برای تولید اعداد اول"""
    
    def __init__(self, limit=100):
        self.limit = limit
        self.primes = []
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if not hasattr(self, 'current'):
            self.current = 2
            self.index = 0
        
        while self.current <= self.limit:
            if self.is_prime(self.current):
                prime = self.current
                self.primes.append(prime)
                self.current += 1
                self.index += 1
                return (self.index, prime)
            self.current += 1
        
        raise StopIteration
    
    def is_prime(self, n):
        """بررسی اول بودن عدد"""
        if n < 2:
            return False
        for i in range(2, int(n ** 0.5) + 1):
            if n % i == 0:
                return False
        return True

# Generator function برای اعداد اول
def prime_generator(limit):
    """Generator function برای تولید اعداد اول"""
    def is_prime(n):
        if n < 2:
            return False
        for i in range(2, int(n ** 0.5) + 1):
            if n % i == 0:
                return False
        return True
    
    count = 0
    num = 2
    
    while num <= limit:
        if is_prime(num):
            count += 1
            yield (count, num)
        num += 1

# استفاده از کلاس
print("=== استفاده از کلاس PrimeGenerator ===")
primes_class = PrimeGenerator(50)
for index, prime in primes_class:
    print(f"{index}. عدد اول: {prime}")
    if index >= 5:  # فقط 5 تا نشان بده
        break

# استفاده از Generator Function
print("\n=== استفاده از Generator Function ===")
for index, prime in prime_generator(30):
    print(f"{index}. عدد اول: {prime}")

# ترکیب با سایر Generator ها
print("\n=== مربع اعداد اول ===")
primes = prime_generator(20)
squared_primes = ((i, p**2) for i, p in primes)

for index, squared in squared_primes:
    print(f"{index}. مربع عدد اول: {squared}")

چالش پیشرفته

یک سیستم پردازش لاگ بسازید که:

  • از Generator برای خواندن فایل‌های بزرگ استفاده کند
  • Pipeline پردازش برای فیلتر و تجزیه لاگ‌ها داشته باشد
  • آمار real-time از داده‌ها ارائه دهد
  • حافظه را بهینه مدیریت کند

این چالش نیاز به ترکیب Generator ها با پردازش متن و مدیریت حافظه دارد. سعی کنید خودتان حل کنید!