Iterators — Python المتقدم
المكرّرات (Iterators)
حلقة for تعمل مع القوائم والنصوص والقواميس والملفات... كيف تعرف Python المرور على كل هذه الأنواع المختلفة؟ الجواب: بروتوكول التكرار.
مصطلحان
- iterable (قابل للتكرار): كائن يمكن المرور عليه: قائمة، نص، قاموس، ملف. فيه الدالة __iter__.
- iterator (مكرّر): كائن يعطي العنصر التالي عند كل طلب، ويتذكر موقعه. فيه __next__.
nums = [10, 20, 30] # iterable
it = iter(nums) # نحصل على iterator
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
next(it) # StopIteration: انتهت العناصرما تفعله for فعلياً
nums = [10, 20, 30]
it = iter(nums)
while True:
try:
item = next(it)
except StopIteration:
break
print(item)المكرّر يُستهلك مرة واحدة
it = iter([1, 2, 3])
print(list(it)) # [1, 2, 3]
print(list(it)) # [] انتهىالقائمة يمكن المرور عليها مراراً لأن كل for تطلب مكرّراً جديداً منها، أما المكرّر نفسه فلا يعود للبداية.
صنع مكرّر خاص
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لماذا هذا مهم؟
المكرّر لا يحتاج كل العناصر في الذاكرة: يحسب العنصر التالي عند الطلب فقط. يمكنك المرور على ملف حجمه 10 جيجابايت سطراً سطراً، أو على تسلسل لا نهائي. هذا ما يسمى التقييم الكسول (lazy evaluation).
أدوات جاهزة: itertools
| الدالة | الوظيفة |
|---|---|
| count(start) | أرقام لا نهائية |
| islice(it, n) | أول n عناصر من أي مكرّر |
| chain(a, b) | المرور على عدة مصادر كأنها واحد |
| groupby(it, key) | تجميع العناصر المتتالية المتشابهة |
| zip، enumerate، map، filter | كلها ترجع مكرّرات كسولة |
تمرين
اكتب صنف EvenNumbers(limit) يعطي الأعداد الزوجية حتى limit، ثم استخدم itertools.islice و itertools.count لطباعة أول 5 مضاعفات للرقم 7.
# فهم الـ iteration
my_list = [1, 2, 3]
my_iter = iter(my_list) # تحويل لـ iterator
print(next(my_iter)) # 1
print(next(my_iter)) # 2
print(next(my_iter)) # 3
# print(next(my_iter)) # StopIteration!
# إنشاء Iterator خاص
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for num in CountDown(5):
print(num) # 5, 4, 3, 2, 1
# Iterator لانهائي
class InfiniteCounter:
def __init__(self, start=0, step=1):
self.current = start
self.step = step
def __iter__(self):
return self
def __next__(self):
value = self.current
self.current += self.step
return value
counter = InfiniteCounter(start=10, step=5)
for _ in range(5):
print(next(counter)) # 10, 15, 20, 25, 30
# Iterator على class مخصص
class Sentence:
def __init__(self, text):
self.words = text.split()
def __iter__(self):
return SentenceIterator(self.words)
class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.words):
raise StopIteration
word = self.words[self.index]
self.index += 1
return word
sentence = Sentence("مرحباً بك في Python")
for word in sentence:
print(word)
# أدوات itertools
from itertools import count, cycle, repeat, chain, islice
# count: عداد لانهائي
for i in islice(count(10), 5):
print(i) # 10, 11, 12, 13, 14
# cycle: تكرار لانهائي
colors = cycle(['أحمر', 'أخضر', 'أزرق'])
for _ in range(5):
print(next(colors))
# chain: دمج iterables
for x in chain([1, 2], [3, 4], [5]):
print(x) # 1, 2, 3, 4, 5اختبر فهمك