يتطلب مستوى: مبتدئ
المكدّس والكومة: ليه المتغيّر المحلي بيموت والكائن بيفضل عايش؟
لو عدّلت نسخة جوه دالة ولقيت الأصل بره اتغيّر، أو ضربت RecursionError فجأة، السبب واحد: مكان تخزين البيانات في الذاكرة. بعد المقال ده هتفرّق بين المكدّس (Stack) والكومة (Heap) في دقيقة، وتعرف تتوقّع سلوك كودك قبل ما تشغّله.
المشكلة باختصار
كل برنامج بيشتغل بيقسّم الذاكرة لمنطقتين رئيسيتين. المتغيّرات المحلية والمكالمات بين الدوال بتعيش في المكدّس. الكائنات الأكبر (القوائم، القواميس، النصوص الطويلة) بتعيش في الكومة. عدم فهم الفرق ده بيسبب باجات محيّرة: قيمة اتغيّرت من غير سبب واضح، أو برنامج اتعلّق من استدعاء متكرر.
مثال بسيط الأول: طاولة المطبخ والمخزن
خلّينا نقرّب الصورة بمثال ملموس. المكدّس زي طاولة صغيرة في المطبخ بتشتغل عليها دلوقتي. كل ما تبدأ خطوة جديدة بتحط ورقة فوق الطاولة، وأول ما تخلّص الخطوة بترمي الورقة اللي فوق فورًا. الطاولة صغيرة، سريعة، ومنظّمة: آخر ورقة دخلت أول ورقة تطلع.
الكومة زي مخزن كبير في آخر البيت. لما يبقى عندك حاجة كبيرة وعايزها تفضل موجودة بعد ما تخلص الخطوة الحالية، بتحطها في المخزن وبتكتب عنوانها على ورقة صغيرة على الطاولة. الورقة بتموت مع نهاية الخطوة، لكن الكرتونة في المخزن بتفضل لحد ما محدش يفضل مسكها.
وبعدين علميًا: إيه اللي بيحصل فعلاً
لما دالة تتنادى، البرنامج بيبني ليها إطار (stack frame) فوق المكدّس فيه متغيّراتها المحلية وعنوان الرجوع. الإطار ده بيتشال تلقائيًا لحظة خروج الدالة. عشان كده المتغيّر المحلي عمره قصير: بيموت مع نهاية الدالة.
لكن لما تعمل كائن زي قائمة، الكائن نفسه بيتخزّن على الكومة. المتغيّر اللي على المكدّس بيمسك عنوان الكائن، مش الكائن نفسه. لو أكتر من متغيّر مسكوا نفس العنوان، أي تعديل بيظهر عند الجميع. وده بالظبط مصدر الباج الشهير في الصورة الجاية.
مثال تنفيذي تجرّبه بنفسك
الكود ده بيوضّح النقطتين مع بعض: المتغيّر المحلي بيختفي، والكائن المشترك بيتعدّل عند الأصل.
def add_tax(prices):
# prices متغير محلي على المكدّس، لكنه يمسك عنوان القائمة على الكومة
prices.append(round(prices[-1] * 0.14, 2)) # تعديل نفس الكائن
total = sum(prices) # total محلي، سيموت مع الدالة
return total
cart = [100, 200]
print(add_tax(cart)) # 342.28
print(cart) # [100, 200, 28.0] <-- الأصل اتغيّر!
# total غير موجود هنا؛ مات مع خروج الدالة
القيمة total عاشت وماتت على المكدّس. لكن cart وprices كانوا بيمسكوا نفس العنوان على الكومة، فالتعديل ظهر بره. لو غيّرت السطر لـ هتعمل كائن جديد على الكومة، والأصل مش هيتغيّر.