🔍 فهم نطاقات المتغيرات (Scopes) في بايثون: دليلك الشامل للمبتدئين

اليوم سنتعرف على مفهوم بالغ الأهمية في البرمجة بشكل عام وفي بايثون بشكل خاص: نطاقات المتغيرات أو Scopes. إذا كنت قد واجهت رسالة خطأ تقول NameError: name 'x' is not defined من قبل، فهذا الدرس سيساعدك على فهم سبب هذا الخطأ وكيف تتجنبه.


🎯 ما هو النطاق (Scope)؟

النطاق هو المنطقة أو السياق في برنامجك حيث يتم تعريف المتغير ويمكن الوصول إليه. تخيل أن النطاق يشبه الغرف في منزلك:

  • بعض الأشياء موجودة في غرفة النوم (محلية) ولا يمكن رؤيتها من الغرفة الأخرى
  • بعض الأشياء موجودة في الصالة (عامة) ويمكن للجميع رؤيتها

في بايثون، النطاق يحدد أين يمكنك استخدام متغير معين في الكود.

# هذا متغير عام (في النطاق العام)
global_var = "أنا متغير عام"

def my_function():
    # هذا متغير محلي (داخل النطاق المحلي للدالة)
    local_var = "أنا متغير محلي"
    print(global_var)  # ✓ يمكن الوصول للمتغير العام
    print(local_var)   # ✓ يمكن الوصول للمتغير المحلي

my_function()
print(global_var)  # ✓ يمكن الوصول للمتغير العام
# print(local_var)  # ❌ خطأ! لا يمكن الوصول للمتغير المحلي خارج الدالة

📍 أنواع النطاقات في بايثون

1. النطاق المحلي (Local Scope)

هو النطاق داخل الدالة. المتغيرات المعرفة داخل دالة تكون محلية وتوجد فقط داخل تلك الدالة.

def calculate_sum(a, b):
    result = a + b  # 'result' متغير محلي
    return result

# print(result)  # ❌ خطأ! 'result' غير معروف خارج الدالة

2. النطاق العام (Global Scope)

هو النطاق خارج أي دالة. المتغيرات المعرفة هنا تكون عامة ويمكن الوصول لها من أي مكان في البرنامج.

website_name = "Codex Academy"  # متغير عام

def welcome_message():
    message = "Welcome to " + website_name  # ✓ يمكن الوصول للمتغير العام
    return message

print(welcome_message())

3. النطاق غير المحلي (Nonlocal Scope)

يظهر عندما يكون لدينا دوال داخل دوال (دوال متداخلة).

def outer_function():
    outer_var = "أنا في النطاق الخارجي"
    
    def inner_function():
        nonlocal outer_var  # نستخدم nonlocal للوصول لمتغير الدالة الخارجية
        outer_var = "تم التعديل من الدالة الداخلية"
    
    inner_function()
    print(outer_var)  # سيطبع: "تم التعديل من الدالة الداخلية"

outer_function()

🧠 قاعدة LEGB: ترتيب البحث عن المتغيرات

عندما تطلب بايثون قيمة متغير، تبحث فيه بالترتيب التالي:

  1. Local - النطاق المحلي (داخل الدالة الحالية)
  2. Enclosing - النطاق المحيط (في الدوال الخارجية)
  3. Global - النطاق العام
  4. Built-in - النطاق المدمج (الدوال المضمنة في بايثون)
x = "عام"  # النطاق العام

def test_scope():
    x = "محلي"  # النطاق المحلي
    print(x)    # سيطبع: "محلي" (يأخذ القيمة من النطاق المحلي)

test_scope()
print(x)        # سيطبع: "عام" (يأخذ القيمة من النطاق العام)

⚠️ الأخطاء الشائعة وكيفية تجنبها

الخطأ: محاولة تعديل متغير عام بدون كلمة global

counter = 0

def increment():
    # counter += 1  # ❌ خطأ! لا يمكن تعديل المتغير العام مباشرة
    global counter  # ✓ الحل الصحيح
    counter += 1

increment()
print(counter)  # سيطبع: 1

الخطأ: الخلط بين المتغيرات المحلية والعامة

name = "Ahmed"

def change_name():
    name = "Mohamed"  # هذا ينشئ متغير محلي جديد، لا يعدل المتغير العام
    print("داخل الدالة:", name)

change_name()  # يطبع: داخل الدالة: Mohamed
print("خارج الدالة:", name)  # يطبع: خارج الدالة: Ahmed

💡 نصائح عملية لاستخدام النطاقات

  1. استخدم متغيرات محلية قدر الإمكان لتجنب التضارب
  2. قلل استخدام المتغيرات العامة إلا عند الضرورة
  3. استخدم أسماء متغيرات واضحة لتجنب الخلط
  4. افهم دائماً أين عرفت المتغير قبل استخدامه
# مثال جيد لاستخدام النطاقات
def calculate_area(radius):
    pi = 3.14159  # متغير محلي مناسب
    area = pi * radius ** 2
    return area

# استخدام الدالة بشكل صحيح
circle_area = calculate_area(5)
print("مساحة الدائرة:", circle_area)

🎓 خلاصة الدرس

  • النطاق المحلي: داخل الدالة، متاح فقط داخلها
  • النطاق العام: خارج الدوال، متاح في كل مكان
  • النطاق غير المحلي: للدوال المتداخلة، نستخدم nonlocal
  • قاعدة LEGB: ترتيب البحث عن المتغيرات
  • تجنب الأخطاء: استخدام global عند تعديل المتغيرات العامة