المستوى المطلوب لقراءة هذا المقال: مبتدئ. لو لسه بتتعلم الدوال (functions) والمتغيرات، المقال ده هيوفّر عليك ساعات تدوير في الأخطاء. هتعرف بالظبط ليه دالة بتعدّل على بياناتك من غير ما تطلب منها كده، وإزاي توقف السلوك ده وقت ما تحب.
الخلاصة الأول: لما تبعت رقم أو نص لدالة، الدالة بتشتغل على نسخة، وبياناتك الأصلية بتفضل سليمة. لكن لما تبعت list أو dictionary، الدالة بتشتغل على نفس الشيء الأصلي، فأي تعديل جوّاها بيظهر برّاها. ده الفرق اللي بيوقّع المبتدئين كل يوم.
المشكلة باختصار
تخيّل إنك كاتب دالة بتضيف عنصر على list علشان تطبع النتيجة بس. بعد ما الدالة تخلص، تلاقي الـ list الأصلية اتغيّرت كمان. انت معملتش return، ومع ذلك البيانات اتبدّلت. الموقف ده مش غلطة في اللغة، ده اسمه التعديل عبر المرجع (pass by reference)، وفهمه بيفرق بين كود بتثق فيه وكود بيفاجئك.
المفهوم بمثال: الورقة والأوضة
تخيّل عندك أوضة فيها صندوق. القيمة (value) معناها إنك بتدّي صاحبك صورة من اللي جوّا الصندوق على ورقة. لو هو شخبط على الورقة، صندوقك مش هيتأثر، لأنه معاه نسخة بس.
المرجع (reference) معناه إنك بتدّي صاحبك مفتاح الأوضة نفسها. لو دخل وغيّر اللي في الصندوق، هتلاقي التغيير لما تدخل انت، لأنكم الاتنين بتفتحوا نفس الأوضة بنفس المفتاح.
دلوقتي بالشكل العلمي: المتغيّر في الذاكرة بيشاور (points) على عنوان. مع الأنواع البسيطة زي الأرقام والنصوص، الدالة بتاخد نسخة من القيمة. مع الأنواع المركّبة زي list و dict، الدالة بتاخد نسخة من العنوان نفسه، فالاتنين بيشاوروا على نفس الكائن (object) في الذاكرة. تعديل محتوى الكائن بيبان عند الطرفين.
كود شغّال يوريك الفرق
الكود ده اتجرّب على Python 3.13. لاحظ إن الرقم رجع زي ما هو، لكن الـ list اتغيّرت:
# رقم: بيتبعت بالقيمة، الأصل ما بيتأثرش
def add_ten(n):
n = n + 10
return n
x = 5
add_ten(x)
print(x) # 5 ← الأصل سليم
# list: بتتبعت بالمرجع، الأصل بيتأثر
def append_item(items):
items.append(99) # تعديل في نفس الكائن
my_list = [1, 2, 3]
append_item(my_list)
print(my_list) # [1, 2, 3, 99] ← اتغيّرت من غير return
نفس السلوك في JavaScript: الـ primitives (number, string, boolean) بالقيمة، والـ objects والـ arrays بالمرجع:
function pushItem(arr) {
arr.push(99); // نفس المصفوفة الأصلية
}
const list = [1, 2, 3];
pushItem(list);
console.log(list); // [1, 2, 3, 99]