Generator ها در پایتون
Generator ها یکی از ویژگی های مهم پایتون هستند که برای ایجاد اشیاء قابل تکرار استفاده می شوند. برخلاف لیست ها، همه مقادیر را همزمان در حافظه نگه نمی دارند و مقادیر را در لحظه تولید می کنند.
yield در مقابل return
تابعی که از return استفاده می کند یکبار اجرا شده و تمام می شود. اما تابعی که از yield استفاده می کند اجرا را متوقف کرده و حالت را حفظ می کند.
# با return - تابع یکبار اجرا میشود و تمام میشود
def with_return():
result = []
for i in range(3):
result.append(i)
return result
# با yield - مقادیر یکییکی برگردانده میشوند
def with_yield():
for i in range(3):
yield i
# خروجی هر دو یکسان است
print(with_return()) # [0, 1, 2]
print(list(with_yield())) # [0, 1, 2]
مثال: شمارش معکوس
def countdown():
i = 5
while i > 0:
yield i
i -= 1
for num in countdown():
print(num)
خروجی:
5
4
3
2
1
استفاده از next() برای دریافت مقادیر
تابع next() مهمترین ابزار برای کار با Generatorهاست. هر بار که next() را صدا بزنید، Generator اجرا میشود تا به yield بعدی برسد و آن مقدار را برگرداند. اگر مقداری نباشد، خطای StopIteration رخ میدهد.
def counter():
yield 10
yield 20
yield 30
gen = counter()
print(next(gen)) # 10
print(next(gen)) # 20
print(next(gen)) # 30
print(next(gen, "پایان")) # پایان
خروجی:
10
20
30
پایان
مثال: تولید اعداد زوج
def even_numbers(n):
for i in range(n):
if i % 2 == 0:
yield i
print(list(even_numbers(10))) # [0, 2, 4, 6, 8]
مثال: خواندن فایل بزرگ
با Generator می توان فایل را خط به خط خواند بدون آنکه کل فایل در حافظه بارگذاری شود.
def read_large_file(filename):
with open(filename, 'r', encoding='utf-8') as file:
for line in file:
yield line.strip()
for line in read_large_file('log.txt'):
if 'ERROR' in line:
print(line)
Generator Expression
مشابه لیست کامپریهنشن، می توان Generator Expression نوشت. تفاوت در پرانتزها است:
# لیست کامپریهنشن - همه مقادیر همزمان ساخته میشوند
squares_list = [x ** 2 for x in range(1000000)]
# Generator Expression - مقادیر بهصورت تنبل ساخته میشوند
squares_gen = (x ** 2 for x in range(1000000))
print(type(squares_list)) # <class 'list'>
print(type(squares_gen)) # <class 'generator'>
مقایسه مصرف حافظه
import sys
# لیست - همه مقادیر در حافظه
numbers_list = [x ** 2 for x in range(10000)]
print(f"List size: {sys.getsizeof(numbers_list):,} bytes")
# خروجی: List size: 87,096 bytes
# Generator - فقط یک عدد در حافظه
numbers_gen = (x ** 2 for x in range(10000))
print(f"Generator size: {sys.getsizeof(numbers_gen):,} bytes")
# خروجی: Generator size: 200 bytes
Generator بیش از ۴۳۵ برابر کمتر از لیست حافظه مصرف می کند.
نکته پایانی
Generator ها برای پردازش داده های حجیم یا بی نهایت انتخابی عالی هستند. اگر نیازی به دسترسی تصادفی به عناصر ندارید، Generator راه حل مناسبی است.