أحيانًا لا تريد معرفة هل يوجد عنصر يحقق شرطًا فقط، ولا تريد الحصول على جميع العناصر المطابقة. المطلوب أبسط من ذلك: أعطني أول عنصر يحقق الشرط وتوقف.
مثلًا لديك قائمة أرقام وتريد أول رقم أكبر من 10، أو قائمة منتجات وتريد أول منتج متوفر، أو قائمة مستخدمين وتريد أول مستخدم يطابق شرطًا معينًا.
في هذا الدرس من سلسلة بايثون بالمثال سنبدأ بالحل الواضح باستخدام for وbreak، ثم نكتب الحل المختصر باستخدام next() مع Generator Expression. وسنشرح أيضًا ماذا يحدث إذا لم يوجد أي عنصر مطابق، وكيف تتجنب StopIteration.
{alertInfo} الخلاصة السريعة: لإيجاد أول عنصر يحقق شرطًا بصورة مختصرة، استخدمnext((item for item in items if condition), default). وجود قيمة افتراضية مثلNoneيمنع ظهورStopIterationإذا لم يوجد تطابق.
{getToc} $title={محتوى المقال}
المشكلة: نريد أول عنصر يحقق شرطًا فقط
لدينا القائمة التالية:
numbers = [5, 7, 12, 4, 18]
ونريد أول رقم أكبر من 10. النتيجة المطلوبة هي:
12
لاحظ أننا لا نريد كل الأرقام الأكبر من 10، لأن هذا سيعطينا [12, 18]. نريد أول تطابق فقط.
الطريقة الأولى: استخدام for مع break
هذه أوضح طريقة للمبتدئ، وهي مهمة لفهم ما الذي ستفعله next() لاحقًا:
numbers = [5, 7, 12, 4, 18]
first_match = None
for number in numbers:
if number > 10:
first_match = number
break
print(first_match)
الناتج:
12
تمر الحلقة على العناصر بالترتيب:
5: لا يحقق الشرط.7: لا يحقق الشرط.12: يحقق الشرط، فنحفظه ثم ننفذbreak.
لن يصل البحث إلى 4 أو 18 لأننا حصلنا بالفعل على أول تطابق.
استخدام for مع else
يمكن كتابة نفس الفكرة باستخدام else مع حلقة for. يتم تنفيذ else إذا انتهت الحلقة بدون الوصول إلى break:
numbers = [5, 7, 12, 4, 18]
for number in numbers:
if number > 10:
first_match = number
break
else:
first_match = None
print(first_match)
هذه صيغة صحيحة، لكنها قد تكون أقل وضوحًا للمبتدئ من تهيئة first_match = None قبل الحلقة.
الطريقة الثانية: استخدام next() مع Generator Expression
الآن يمكن كتابة الحل بصورة أقصر:
numbers = [5, 7, 12, 4, 18]
first_match = next(
(number for number in numbers if number > 10),
None
)
print(first_match)
الناتج:
12
الجزء:
(number for number in numbers if number > 10)
هو Generator Expression. لا ينشئ قائمة جديدة تحتوي على جميع النتائج أولًا، بل ينتج العناصر تدريجيًا عند طلبها.
ثم تطلب next() العنصر التالي من هذا الـGenerator. وبما أن الـGenerator لا ينتج إلا العناصر التي تحقق الشرط، تكون القيمة التي تحصل عليها هي أول عنصر مطابق.
{alertSuccess}
هذا هو السبب في أن next() مع Generator Expression مناسبة لهذه المشكلة: نحن نحتاج أول تطابق فقط، ولا نحتاج بناء قائمة بكل النتائج المطابقة.
ماذا تعني next() في بايثون؟
الدالة المدمجة next() تحصل على العنصر التالي من Iterator.
مثلًا:
numbers = [10, 20, 30]
iterator = iter(numbers)
print(next(iterator))
print(next(iterator))
الناتج:
10
20
لاحظ أننا استخدمنا iter(numbers) أولًا. القائمة نفسها Iterable، لكنها ليست Iterator يمكنك تمريرها مباشرة إلى next().
أما Generator Expression فهو Generator Iterator، لذلك يمكن تمريره مباشرة إلى next().
ماذا يحدث إذا لم يوجد عنصر يحقق الشرط؟
هذه نقطة مهمة جدًا. إذا استخدمت next() بدون قيمة افتراضية ولم ينتج الـIterator أي عنصر آخر، يظهر الاستثناء:
StopIteration
مثل هذا الكود:
numbers = [1, 3, 5, 7]
first_even = next(
number
for number in numbers
if number % 2 == 0
)
لا يوجد رقم زوجي، ولذلك لا توجد قيمة تستطيع next() إرجاعها.
استخدم القيمة الافتراضية لتجنب StopIteration
الصيغة الرسمية لـnext() تسمح بتمرير قيمة افتراضية ثانية:
next(iterator, default)
إذا انتهى الـIterator بدون قيمة، تُعاد القيمة الافتراضية بدل رفع StopIteration.
numbers = [1, 3, 5, 7]
first_even = next(
(number for number in numbers if number % 2 == 0),
None
)
print(first_even)
الناتج:
None
لهذا السبب سأستخدم في أغلب أمثلة هذا المقال:
next((...), None)
مثال عملي: إيجاد أول منتج متوفر
تظهر فائدة الطريقة أكثر مع بيانات حقيقية مثل List of Dictionaries:
products = [
{"name": "Keyboard", "stock": 0},
{"name": "Mouse", "stock": 5},
{"name": "Monitor", "stock": 3}
]
available_product = next(
(
product
for product in products
if product["stock"] > 0
),
None
)
print(available_product)
الناتج:
{'name': 'Mouse', 'stock': 5}
لم يعد البرنامج يحتاج إلى بناء قائمة بكل المنتجات المتوفرة. وجد أول منتج مخزونه أكبر من صفر وتوقف عنده.
مثال: إيجاد أول نص يحقق شرطًا
يمكن استخدام نفس الطريقة مع النصوص:
names = ["Ali", "Omar", "Sara", "Mohammed"]
first_long_name = next(
(name for name in names if len(name) >= 5),
None
)
print(first_long_name)
الناتج:
Mohammed
الشرط هنا هو أن يكون طول الاسم 5 أحرف أو أكثر.
هل next() تفحص كل القائمة؟
ليس بالضرورة. Generator Expression تعمل بصورة Lazy؛ أي إن القيم يتم إنتاجها عند الحاجة. وعندما تحصل next() على أول قيمة مناسبة، لا تحتاج إلى طلب بقية العناصر.
يمكن رؤية ذلك بوضوح:
def is_match(number):
print("Checking:", number)
return number > 10
numbers = [5, 7, 12, 4, 18]
first_match = next(
(number for number in numbers if is_match(number)),
None
)
print("Result:", first_match)
الناتج:
Checking: 5
Checking: 7
Checking: 12
Result: 12
لاحظ أن 4 و18 لم يتم فحصهما، لأن أول تطابق ظهر عند 12.
next() أم List Comprehension ثم [0]؟
قد ترى هذا الحل:
matches = [number for number in numbers if number > 10]
first_match = matches[0]
هو يعمل إذا كان هناك تطابق، لكنه ليس الخيار الأفضل عندما تريد العنصر الأول فقط:
- ينشئ قائمة بكل العناصر المطابقة، مع أنك تحتاج إلى عنصر واحد.
- إذا كانت القائمة الناتجة فارغة، سيظهر
IndexErrorعند استخدام[0].
أما:
next((number for number in numbers if number > 10), None)
فتعبر مباشرة عن المطلوب: أعطني أول عنصر مطابق، أو None إذا لم يوجد.
next() أم filter()؟
filter() تعيد Iterator أيضًا، لذلك يمكن استخدام:
numbers = [5, 7, 12, 4, 18]
first_match = next(
filter(lambda number: number > 10, numbers),
None
)
print(first_match)
وهذا يعطي:
12
الطريقتان Lazy ويمكن أن تتوقفا عند أول تطابق. إذا كان الشرط مكتوبًا مباشرة داخل السطر، فأنا أفضل Generator Expression لأنها تجعل الشرط واضحًا في مكانه:
next((number for number in numbers if number > 10), None)
أما إذا كانت لديك دالة شرط جاهزة أصلًا، فقد يكون filter() مناسبًا.
كيف أجد index لأول عنصر يحقق الشرط؟
إذا كنت تريد موضع أول عنصر بدل العنصر نفسه، ادمج enumerate() مع Generator Expression:
numbers = [5, 7, 12, 4, 18]
first_index = next(
(
index
for index, number in enumerate(numbers)
if number > 10
),
None
)
print(first_index)
الناتج:
2
لأن الرقم 12 موجود في الفهرس 2.
مشكلة استخدام None كقيمة افتراضية
None مناسبة جدًا عندما تعرف أن عناصر قائمتك لن تكون None. لكن ماذا لو كانت None نفسها قيمة صحيحة يمكن أن تحقق الشرط؟
عندها لن تستطيع التمييز بين:
- وجدنا العنصر
None. - لم نجد أي عنصر وأعدنا
Noneكقيمة افتراضية.
في هذه الحالات يمكنك إنشاء Sentinel خاص:
items = [None, "Python", "Django"]
MISSING = object()
result = next(
(item for item in items if item is None),
MISSING
)
if result is MISSING:
print("No match")
else:
print("Matched:", result)
الناتج:
Matched: None
هذه ليست خطوة ضرورية لمعظم أمثلة المبتدئين، لكنها مهمة عندما تبني دالة عامة أو تتعامل مع بيانات قد تحتوي على None.
جدول مقارنة بين الطرق
| الطريقة | الميزة | الملاحظة |
|---|---|---|
for + break |
واضحة وسهلة التعديل | أفضل بداية لفهم الفكرة |
next() + Generator Expression |
مختصرة ولا تحتاج قائمة بكل النتائج | ممتازة عندما تريد أول تطابق فقط |
next(filter(...)) |
Lazy أيضًا | مفيدة إذا كان لديك Predicate جاهز |
List Comprehension + [0] |
سهلة الفهم | تبني كل النتائج وقد تسبب IndexError |
ما الطريقة التي أنصح بها؟
إذا كنت ما زلت تتعلم الحلقات والشروط، ابدأ بـfor + break لأنك سترى منطق البحث بوضوح.
بعد فهم الفكرة، استخدم:
first_match = next(
(item for item in items if condition),
None
)
عندما يكون المطلوب ببساطة هو أول عنصر يحقق الشرط.
{alertInfo} لا تستخدمnext()لمجرد جعل الكود أقصر. إذا كان الشرط أو منطق البحث معقدًا ويحتاج عدة خطوات، قد تكون حلقةforأكثر وضوحًا وأسهل للصيانة.
الفرق عن البحث العادي داخل List
هذا الدرس يجيب عن سؤال:
ما أول عنصر يحقق شرطًا؟
وهو مختلف عن سؤال:
هل قيمة معينة موجودة داخل القائمة؟
إذا كنت تريد فقط فحص وجود قيمة محددة أو معرفة موقعها باستخدام in وindex()، راجع البحث عن عنصر داخل List في بايثون. الفصل بين النيتين مهم حتى تستخدم الأداة المناسبة لكل مشكلة.
أخطاء شائعة مع next()
1. تمرير List مباشرة إلى next()
هذا خطأ:
next([10, 20, 30])
لأن next() تتطلب Iterator. استخدم iter() أو مرر Generator Expression.
2. نسيان القيمة الافتراضية
إذا كان عدم وجود تطابق حالة متوقعة في برنامجك، استخدم قيمة افتراضية لتجنب StopIteration.
3. بناء قائمة كاملة ثم أخذ أول عنصر
إذا كنت تريد أول تطابق فقط، لا تحتاج إلى إنشاء جميع النتائج أولًا.
4. استخدام None عندما تكون None قيمة صحيحة
استخدم Sentinel خاصًا عندما تحتاج إلى التمييز بين «لا يوجد تطابق» وبين القيمة None نفسها.
5. جعل Generator Expression معقدة جدًا
إذا أصبح الشرط طويلًا أو يحتوي منطقًا متعدد الخطوات، انقل الشرط إلى دالة أو استخدم حلقة for أكثر وضوحًا.
تمرين بسيط
لديك قائمة درجات:
scores = [45, 58, 72, 66, 90]
اكتب كودًا باستخدام next() يعيد أول درجة تساوي 70 أو أكثر.
النتيجة المطلوبة:
72
ثم جرّب قائمة لا تحتوي أي درجة ناجحة، واجعل النتيجة None بدل ظهور خطأ.
روابط مفيدة من بايثون العرب
- البحث عن عنصر داخل List في بايثون
- شرح Lists في بايثون للمبتدئين
- شرح الحلقات for وwhile وrange
- شرح List Comprehension في بايثون
مصادر رسمية للتوسع
الخلاصة
لإيجاد أول عنصر يحقق شرطًا في List لديك خيار واضح للمبتدئ باستخدام for + break، وخيار مختصر وعملي باستخدام next() مع Generator Expression.
- استخدم
for + breakإذا أردت منطقًا واضحًا أو خطوات إضافية أثناء البحث. - استخدم
next((item for item in items if condition), default)إذا أردت أول تطابق فقط. - أضف قيمة افتراضية إذا كان من الممكن ألا يوجد أي تطابق.
- تذكر أن
next()تعمل مع Iterator، وGenerator Expression تعطيك Iterator مناسبًا لهذه المهمة.
{alertSuccess} احفظ هذه الصيغة:
next((item for item in items if condition), None)
وستغطي بها عددًا كبيرًا من حالات «أعطني أول عنصر يحقق هذا الشرط».
أسئلة شائعة
كيف أجد أول عنصر يحقق شرطًا في List باستخدام بايثون؟
يمكنك استخدام حلقة for مع break، أو استخدام next() مع Generator Expression مثل next((x for x in items if condition), None).
لماذا نستخدم next() مع Generator Expression؟
لأن Generator Expression تنتج العناصر عند الطلب، وnext() تحتاج إلى أول عنصر فقط، لذلك لا نحتاج إلى بناء قائمة تحتوي على جميع العناصر المطابقة.
ماذا يحدث إذا لم يجد next() أي عنصر؟
إذا لم تمرر قيمة افتراضية، ترفع next() الاستثناء StopIteration. وإذا مررت قيمة ثانية مثل None فستعيدها عند انتهاء الـIterator بدون تطابق.
هل يمكن تمرير List مباشرة إلى next()؟
لا. next() تتطلب Iterator. يمكنك استخدام iter(list) أو تمرير Generator Expression، وهي Generator Iterator.
هل next() أفضل من filter()؟
ليس دائمًا. filter() تعيد Iterator ويمكن استخدامها مع next(). Generator Expression غالبًا أوضح عندما يكون الشرط مكتوبًا مباشرة داخل التعبير.
كيف أجد index لأول عنصر يحقق شرطًا؟
استخدم enumerate() داخل Generator Expression ثم مررها إلى next()، مثل next((i for i, x in enumerate(items) if condition), None).
هل next() تفحص جميع العناصر؟
عند استخدامها مع Generator Expression، يتم إنتاج القيم تدريجيًا. بمجرد العثور على أول عنصر يحقق الشرط وإعادته، لا تحتاج next() إلى استهلاك بقية الـGenerator لهذا الطلب.



