List Comprehension vs Generator Expression

في هذا المقال سنتعلم معًا بعض المزايا الجميلة في بايثون التي ستساعدك حتمًا على توفير الاسطر البرمجية وبالتالي السرعة في المعالجة

Miss-xمنذ 6 سنوات

بسم الله الرحمن الرحيم 

في هذا المقال سنتعرف على : 

  • ماهي List Comprehension

  • امثلة مباشرة على List Comprehension

  • ماهو Generator Expressions

  • Iterable and Iterator

  • كيف نتعامل مع Generator Expressions

المتطلبات على الأقل ان يكون لدى القارىء : 

معرفة بانواع البيانات في بايثون والتكرارات

المستوى : 

مبتدىء الى متوسط ولكن ان كنت في مستوى متقدم ربما لن يقدم لك تلك الفائدة  المرجوة !


ماهي List Comprehension ؟

لناخد مثال بسيط اولًا فرضًا طلب مني القيام بكتابة كود برمجي يقوم بإضافة الاعداد من 0 الى 9 الى List فارغة.

num = []
for i in range(10):
     num.append(i)  
print(num)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]#output

ولكن باستخدام List Comprehension  سنقوم بحل المثال كالتالي :    

num = [i for i in range(10)]
print(num)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]#output 

 
كما نلاحظ هنا تماما اختصرنا كود بثلاث اسطر بسطر واحد اذا يمكننا القول ببساطة  ان List Comprehension  هي احد طرق بايثون لجعل انشاء List انيق وبسطور اقل وسهل القراءة وسريع 

والصيغة العامة لها كالتالي : 

list_variable = [x for x in iterable] 


حيث أن : 

  • list_variable 

     هي المتغير الذي نريد حفظ List داخله 

تعتبر عن القيمة التي سنقوم باضافتها ل List 

  • for x in iterable 

       تعني شرط التكرارسنتحدث باالتفصيل الممل عن  iterable  لاحقًا 


Multiple loops مع list comprehension

فرضا اردنا تكرار عناصر قائمة معينة 5 مرات بالطريقة العادية سنقوم بحلها هكذا : 

our_list = [0,1,2,3]
num = []
for i in range(5):
      for j in our_list:
            num.append(j)               
print(num)
 
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]#output

ولكن مع استخدام List Comprehension نقوم بالتالي : 

num = [j for i in range(5) for j in our_list]
print(num)

[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]#output

 

IF conditions مع list comprehension

فرضا اردنا اضافت شرط لمثالنا الاول بحيث انه سيقوم بعمل List للاعداد الزوجية الواقعة بين  0 و 9  بلطريقة العادية سنقوم بالتالي :

num = []
for i in range(10):
        if i % 2 == 0 :
            num.append(i)
print(num)

[0, 2, 4, 6, 8] #output

 اما مع List Comprehension سنقوم بحل المثال بهذه الطريقة :     

num = [ i for i in range(10) if i % 2 == 0 ]
print(num)

[0, 2, 4, 6, 8]#output 

ملاحظة : Comprehension ليست مخصصة فقط لـ List بل يمكن استخدامها مع tuple , dictionaries وهناك ماسيواجهنا خصوصا عندما نتعامل مع tuple 
مثال : 

num = [ i for i in range(10) if i % 2 == 0 ]
print(num)
[0, 2, 4, 6, 8] #output 
num = ( i for i in range(10) if i % 2 == 0 )
print(num)
<generator object <genexpr> at 0x00000244EE93C360> #output 

نلاحظ هنا ان الاول يعتبر List Comprehension وعرفنا ذلك من خلال الاقواس [] بينما الثاني يملك الاقواس ()  والتي يقوم بايثون بمعالجتها على انها tuple اذا لماذا لم يتم طباعة المخرجات مثل مخرجات list؟ وماهي generator ؟وكيف سنقوم بحل هذه المشكلة ؟ 

هذا الامر يقودونا الى مايسمى بـ Generator Expression

 

ماهي Generator Expression؟

تعتبر Generator من الاكثر الاشياء الجميلة في بايثون رغم أنها سهلة ولكن هناك تعقيد بسيط في فهمها جيدا ولها عدة انواع ولكن بهذا الموضوع سنتطرق لنوع واحد وهو Generator Expressions

فيحديثنا السابق عن  List Comprehensionsعلمنا ان Comprehensions ليست محصورة فقط على list حيث انه يمكن ان نقوم بعمل مشابهه ل tuple ولكن صادفتنا هناك مشكلة اذ مانتج كانمختلف تماما عن المتوقع ! 

ان الناتج الذي نتج انذاك يسمى Generator Expressions  

ولكن قبل المضي قدما يجب لتسهيل فهمنا Generator Expressions  

التعرف على :

Iterable and Iterator
1- Iterable
هو object ببايثون يقوم بارجاع  Iterator وتحتوي على ولها احد الخواص iter او  getitem 
اذا كان ماقلته غير مفهوم بالنسبة لك لاداعي للذعر ابدا  انت تعرفه مسبقا! ولكن هيا بنا لنضع النقاط على الحروف 😉
 
يعتبر list احد صور Iterable نعم هناك صور اخرى له ايضا  ستعرفها بنفسك 😉  لنبسط الفكرة قليلا ياترى مالذي يفرق list عن int ؟

list = [1,2,3] 

print(list[0]) 

1 #output

num = 123
 
print(num[0])
 

#output    
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
num[0]
TypeError: 'int' object is not subscriptable
    

اذا مايفرق list عن int انه قابل للفهرسة او يمكن ان يحتوي على index كما نعلم اذا يمكننا صياغة  الان Iterable  انها اي object قابل ان يكون له index

مثلا : list , string , dictionary , tuple , .. etc  

ويمكننا المعرفة عن طريق استخدام احد الدوال
hasattr() 
حيث انا قلنا سابقا شرط ان يكون object الذي نملكه يجب ان يكون له احد الخواص iter او  getitem 

>>> hasattr(list, '__iter__')
True
>>> hasattr(int, '__iter__')
False

2- Iterator 

اذا ماهو Iterator ؟ ممكن ان نقول Iterator هي القيم التي لها index داخل Iterable 

3- Iteration
لنفترض اردنا طباعة كل قيم اللستة التي عملنها مسبقا  سنقوم بالتالي : 

for i in list :
        print(i)

#output      
1
2
3


اذا يمكننا تعريف  Iteration ببساطة على انها العملية التي تاخد القيم من شيء معين 
يمكننا ان نربط هذه المعاني بصفحات الكتاب بحيث نعتبر Iterable صفحات الكتاب  و Iteration  فاصل الكتاب و Iterator  احدى الصفحات التي وضعنا بها فاصل الكتاب 
في مثالنا السابق عندما اردنا طباعة قيم اللستة ان ماحصل تقريبا خلف الانظار هو كالتالي : 
1- تم استدعاء دالة تسمى iter()  لتحويل ال object الى iterator object  
2-  تم استدعاء الدالة next() لتقوم باخد القيمة الاخرى داخل iterator object 
3- StopIteration exception وهو ماحصل عندما انتهت القيمة عندي وتوقف for loop 

>>> list = [1,2,3]
>>> iterator = iter(list)
>>> print(iterator)
<list_iterator object at 0x000001CA788C8710>
>>> next(iterator)
1
>>> next(iterator)
2
>>> next(iterator)
3
>>> next(iterator)
Traceback (most recent call last):
  File "<pyshell#82>", line 1, in <module>
    next(iterator)
StopIteration 

كما لاحظنا هنا عندما استدعينا دالة iter() قامت بتحويل list object الى iterator object 

وثم عندما استدعينا next() قامت بطرح القيمة مرا تل والاخرى الى ان انتهت من القيم قامت بطباعة خطا StopIteration اي انه لايجود قيم يمكن طباعته 

ملاحظة : لطباعة جميع القيم بسطر واحد يمكن استخدام *  بالمثال السابق  (print(*iterator  ولكن يجب التاكد اننا اعدنا جعل المتغير  iterator object حيث بالخطوة الاولى سنقوم بالتفريغ الكلي وهذا ماسنناقشه لاحقا 

اعتقد انه حان الوقت للتعرف على   Generator Expressions 

نلاحظ عندما قمنا بطابعة iterator object ظهر لي
 <list_iterator object at 0x000001CA788C8710> 
 وهو مشابهه تقريبا لما حصل معي عند استخدام tuple  كما تلاحظون 

num = [ i for i in range(10) if i % 2 == 0 ]
print(num)

[0, 2, 4, 6, 8] #output

num = ( i for i in range(10) if i % 2 == 0 )
print(num)

<generator object <genexpr> at 0x00000244EE93C360> #output

  
اذا الان اتضحت الرؤية  ان الحالة الاولى كانت List Comprehension والحالة الثانية كانت Generator Expressions  وهي كسابقتها  iterator object
 
اذا كيف سنقوم بحل المشكلة وطباعة القيم ؟

هناك حلان : 

1. سنقوم بماقمنا به  بالمثال السابق بعد عمل iterator object وهي استخدام دالة next() 

>>> num = ( i for i in range(10) if i % 2 == 0 )
>>> next(num)
0
>>> next(num)
2
>>> next(num)
4
>>> next(num)
6
>>> next(num)
8
>>> next(num)
Traceback (most recent call last):
  File "<pyshell#97>", line 1, in <module>
    next(num)
StopIteration

2- او اننا سنقوم باستخدام for loop 

>>> num = ( i for i in range(10) if i % 2 == 0 )
>>> for i in num :
        print(i)

  
0
2
4
6
8

حسنا الان  ماذا لو قمنا بعمل for loop اخرى ل num ؟ 
بو o.O اختفى كل شيء ! 
نعم لهذا بالمثال السابق قلت انه من الجدير ان نقوم بتعريف المتغير مره اخرى وهذا مايميز Generator Expressions اي  ان القيمة التي سوف نقوم بطباعتها سيتم محوها من الذاكرة على الفور مما يجعل الامر كود اقل ومساحة اقل وعمل لا اروع 😇 

 

الملخص 

  • - كما ذكرنا سابقا List Comprehension تعتبر طريقة سهلة وانيقة لانشاء list 

  • - وايضا تعتبر طريقة اسرع من الطرق العادية كاستخدام for loop مثلا 

  • - يجب الانتباه جيدا عند استخدام List Comprehension بحيث لايتعدى الكود سطر واحد حتى يحتفظ الكود على سهولة قرائته 

  • - يجب كتابة كل List Comprehension ب for loop ولكن لايمكن تحويل كل for loop الى List Comprehension 

  • - لاتعتبر List Comprehension الطريقة الوحيدة هناك طرق اخرى لتحسين طريقة كتابة list اما باستخدام  Lambda functions او built in functions وغيرهم 

  • -Generator Expressions تشبهه List Comprehension ولكن الاختلاف في الناتج 

  • -Generator Expressions اذا تم استخدامها مره  واحدة لايمكن استخدامها مرا اخرى 

 

وكما قلت في بادىء الامر توجد انواع ومواضع مختلفه يستخدم لها Generator مثلا function generator او class-based iterator وجميعها دا فائدة قصوى في بعض الحالات يمكنكم البحث عنهم والتعمق فيهم او ربما ساقوم بطرح مقال اخر يخص هذا الجانب من Generator  بشكل اعمق 

 

بالتوفيق جميعَا 

 

 

ياصديقي الأمر كله يدور حول الشغف ،ما إن يبهت الشغف يبهت معه كل شيء !

miss-x⭐️

 

5
إعجاب
1448
مشاهدات
2
مشاركة
2
متابع

التعليقات (1)

Linuxmoon:

مقال رائع شكراا لك

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !