حل خطأ UnboundLocalError في Python | مشكلة وحل 21

حل خطأ UnboundLocalError local variable referenced before assignment في Python

يظهر خطأ UnboundLocalError في Python عندما تعتبر اللغة اسمًا متغيرًا محليًا داخل دالة، ثم يحاول الكود قراءته قبل أن يحصل على قيمة في ذلك النطاق. أكثر الحالات شيوعًا هي تعديل متغير خارجي داخل الدالة، أو استخدام += قبل التهيئة، أو تعريف المتغير داخل if أو try قد لا يُنفذ.

قد تظهر الرسالة بالصيغة القديمة التالية:

UnboundLocalError: local variable 'count' referenced before assignment

وفي إصدارات Python الحديثة تظهر غالبًا بصيغة قريبة:

UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

في الصيغتين السبب الأساسي واحد: إذا وجدت Python إسنادًا إلى الاسم داخل الدالة مثل count = ... أو count += 1، فهي تتعامل معه عادةً كاسم محلي في الدالة كلها. إذا احتاج الكود إلى قراءة هذا الاسم قبل أن يحصل على قيمة محلية، يظهر UnboundLocalError. الحل الصحيح يعتمد على نيتك: تهيئة متغير محلي، تمرير القيمة وإرجاعها، أو استخدام global أو nonlocal عندما تريد فعلًا تعديل متغير من نطاق خارجي.

{alertInfo} الفكرة ببساطة: وجود إسناد إلى اسم داخل دالة قد يجعل Python تعتبره محليًا في ذلك النطاق. لا تقفز مباشرة إلى global: إذا كان المتغير محليًا فابدأ بتهيئته، وإذا كان قيمة تدخل إلى الدالة فمررها وأعد النتيجة، واستخدم global أو nonlocal فقط عندما يكون تعديل النطاق الخارجي مقصودًا.

{getToc} $title={محتوى المقال}

هذا هو الدرس 21 من سلسلة مشكلة وحل في بايثون، ويركز على تشخيص UnboundLocalError وفهم أسبابه الشائعة داخل الدوال ثم اختيار طريقة الإصلاح المناسبة.

ما معنى UnboundLocalError في Python؟

UnboundLocalError هو نوع خاص من NameError. يظهر عندما تحاول دالة استخدام متغير محلي قبل أن ترتبط به قيمة. بمعنى أبسط: Python تعرف أن الاسم تابع للدالة الحالية، لكنها لم تجد له قيمة وقت استخدامه.

جزء الرسالةمعناه
UnboundLocalErrorالمتغير اعتُبر محليًا داخل الدالة، لكنه لم يحصل على قيمة محلية بعد.
local variablePython تتعامل مع الاسم كمتغير تابع للدالة الحالية.
referenced before assignmentتمت قراءة المتغير قبل تنفيذ سطر يعطيه قيمة.

ما معنى cannot access local variable where it is not associated with a value؟

هذه هي الصيغة الحديثة الشائعة لرسالة UnboundLocalError. معناها لا يختلف عن عبارة local variable referenced before assignment: الاسم اعتُبر محليًا داخل الدالة، لكن التنفيذ وصل إلى قراءة الاسم قبل أن ترتبط به قيمة محلية.

لذلك لا تبحث عن حل مختلف لمجرد تغيّر نص الرسالة. افحص مكان الإسناد إلى المتغير، وجميع مسارات if وtry، ثم اختر طريقة الإصلاح المناسبة.

أبسط مثال يسبب الخطأ

score = 10

def increase_score():
    print(score)
    score = score + 1

increase_score()

قد تتوقع أن يطبع الكود 10 ثم يزيد القيمة، لكنه سيظهر خطأ. السبب أن السطر التالي يحتوي على إسناد إلى score داخل الدالة:

score = score + 1

لذلك Python تقرر أن score محلي في هذه الدالة كلها. وعندما تصل إلى print(score)، تحاول قراءة المتغير المحلي قبل أن يحصل على قيمة.

أسرع طريقة لاختيار حل UnboundLocalError

سبب الخطأالحل الأنسب غالبًا
استخدام += أو قراءة متغير محلي قبل تهيئتهأعطِ المتغير قيمة أولية داخل الدالة قبل استخدامه.
تعريف المتغير داخل if أو try فقطغطِّ جميع مسارات التنفيذ بقيمة افتراضية أو return واضح.
تريد تعديل متغير موجود في مستوى الملفيفضل تمرير القيمة وإرجاع النتيجة، أو استخدم global إذا كان تعديل المتغير العام مقصودًا.
تريد تعديل متغير في دالة خارجية محيطةاستخدم nonlocal داخل الدالة المتداخلة.

هذه الحالات الأربع تغطي معظم أسباب الخطأ. الأقسام التالية تشرح لماذا تحدث وكيف تختار الحل بدون تحويل كل متغير إلى global.

ما هو Scope أو نطاق المتغير؟

نطاق المتغير scope يحدد المكان الذي يمكن الوصول إلى الاسم منه. هنا نحتاج فقط إلى القدر الذي يفسر UnboundLocalError: المتغير المحلي والعام والمتغير في الدالة المحيطة. ولشرح أوسع مستقل راجع شرح Scope والمتغيرات Local وGlobal في بايثون.

النوعأين يُعرّف؟مثال
متغير عام globalخارج الدوال في مستوى الملف.score = 10
متغير محلي localداخل دالة.name = "Ali" داخل def.
متغير من نطاق خارجي nonlocalداخل دالة متداخلة، ويخص الدالة المحيطة.متغير داخل outer() تستخدمه inner().

يمكنك قراءة متغير عام داخل دالة ما دمت لا تعيد إسناد قيمة إليه داخلها. أما إذا عدلته أو كتبت += عليه، يجب أن تحدد بوضوح ما إذا كنت تريد متغيرًا محليًا أم متغيرًا خارجيًا.

الفرق بين المتغير المحلي والمتغير العام في Python وسبب UnboundLocalError

لماذا لا يظهر الخطأ عند القراءة فقط؟

site_name = "بايثون العرب"

def show_site_name():
    print(site_name)

show_site_name()

هذا الكود يعمل لأن الدالة تقرأ site_name فقط، ولا توجد داخلها عملية إسناد إلى الاسم نفسه.

لكن عند كتابة:

site_name = "بايثون العرب"

def change_site_name():
    site_name = "Arab Python"
    print(site_name)

change_site_name()
print(site_name)

لن يحدث خطأ هنا، لكن القيمة الجديدة داخل الدالة محلية ومستقلة، لذلك يبقى المتغير الخارجي كما هو:

Arab Python
بايثون العرب

السبب الأكثر شيوعًا: استخدام += داخل الدالة

الاختصار += يتضمن عملية إسناد. لهذا يسبب الخطأ عند استخدامه على متغير خارجي دون توضيح:

visits = 0

def add_visit():
    visits += 1
    print(visits)

add_visit()

تتعامل Python مع visits هنا كمتغير محلي، لكنها لا تملك قيمة محلية له قبل محاولة الزيادة.

الحل الأفضل غالبًا: مرّر القيمة وأعد النتيجة

في أغلب البرامج، الأفضل ألا تعتمد على متغير عام. مرّر القيمة إلى الدالة ثم أعد القيمة الجديدة. هذا يجعل الكود أوضح وأسهل في الاختبار.

def increase_score(score):
    return score + 1

score = 10
score = increase_score(score)

print(score)
11

حل آخر: هيّئ المتغير المحلي قبل استخدامه

إذا كان المتغير يجب أن يكون محليًا داخل الدالة، أعطه قيمة أولية قبل استخدام +=.

مثال خاطئ

def calculate_total(prices):
    for price in prices:
        total += price

    return total

الحل

def calculate_total(prices):
    total = 0

    for price in prices:
        total += price

    return total

print(calculate_total([10, 20, 30]))
60

هذه من أكثر الحالات تكرارًا: كتابة count += 1 أو total += price قبل تهيئة المتغير داخل النطاق المناسب.

حل استخدام متغير محلي قبل إعطائه قيمة داخل دالة Python

هل أستخدم global لحل الخطأ؟

يمكن استخدام global عندما تحتاج فعلًا إلى تعديل متغير عام من داخل دالة. لكنه ليس الحل الافتراضي، لأن كثرة المتغيرات العامة تجعل البرنامج أصعب في الفهم والتتبع.

visits = 0

def add_visit():
    global visits

    visits += 1
    print(f"عدد الزيارات: {visits}")

add_visit()
add_visit()
عدد الزيارات: 1
عدد الزيارات: 2
{alertWarning} تنبيه: لا تستخدم global فقط لإصلاح الخطأ بسرعة. في أغلب الدوال، تمرير القيم وإرجاع النتائج أوضح وأكثر قابلية للصيانة.

متى نستخدم nonlocal؟

تستخدم nonlocal مع الدوال المتداخلة. وهي تعني أن المتغير موجود في الدالة الخارجية المحيطة، وليس في مستوى الملف كله.

def create_counter():
    count = 0

    def increase():
        nonlocal count
        count += 1
        return count

    return increase

counter = create_counter()
print(counter())
print(counter())
1
2

بدون nonlocal count ستعامل Python المتغير داخل increase() كمتغير محلي جديد، ثم يظهر UnboundLocalError.

استخدام global وnonlocal لحل UnboundLocalError في Python

الفرق بين NameError وUnboundLocalError

الخطأمتى يظهر؟مثال
NameErrorعندما لا تجد Python الاسم في النطاقات المتاحة أصلًا.print(user_name) دون تعريف user_name.
UnboundLocalErrorعندما يكون الاسم محليًا داخل الدالة، لكنك استخدمته قبل إعطائه قيمة.count += 1 قبل تهيئة count.

بمعنى آخر: UnboundLocalError هو حالة أكثر تحديدًا من NameError. إذا كانت المشكلة أن الاسم غير معرّف أصلًا، فراجع حل خطأ NameError: name is not defined في بايثون.

أخطاء شائعة تسبب UnboundLocalError

1. تعريف متغير داخل شرط قد لا يعمل

def get_discount(is_member):
    if is_member:
        discount = 20

    return discount

عندما تكون is_member بقيمة False، لا ينفذ الشرط ولا يحصل discount على قيمة. الحل هو إعطاء قيمة افتراضية قبل الشرط:

def get_discount(is_member):
    discount = 0

    if is_member:
        discount = 20

    return discount

2. تعريف متغير داخل try ثم استخدامه بعد except

هذه حالة شائعة مع try/except: إذا وقع الاستثناء قبل سطر الإسناد، فلن يحصل المتغير على قيمة. إذا كان التعامل مع الاستثناءات جديدًا عليك، راجع شرح try وexcept في Python.

def read_age(text):
    try:
        age = int(text)
    except ValueError:
        print("العمر غير صالح")

    return age

إذا حدث ValueError، لن يحصل age على قيمة. الحل أن تعيد قيمة مناسبة من except:

def read_age(text):
    try:
        return int(text)
    except ValueError:
        print("العمر غير صالح")
        return None

وإذا كنت تحتاج إلى تنفيذ كود يعتمد على القيمة فقط عند نجاح كتلة try، فيمكن وضع هذا الكود داخل else بدل استخدام متغير قد لا يكون قد أُسندت إليه قيمة.

3. استخدام += أو -= قبل القيمة الأولية

لا تكتب count += 1 أو total += price إلا بعد تهيئة المتغير داخل النطاق الصحيح.

4. توقع أن if أو try تعمل دائمًا

إذا عرفت متغيرًا في مسار واحد فقط من التنفيذ، اسأل: ماذا يحدث في المسار الآخر؟ ضع قيمة افتراضية أو أعد نتيجة واضحة من كل فرع.

أخطاء شائعة تسبب UnboundLocalError في Python

مثال عملي كامل: حساب إجمالي طلب بأمان

def calculate_order_total(prices):
    total = 0

    for price in prices:
        total += price

    return total

order_prices = [15, 25, 40]

print(f"إجمالي الطلب: {calculate_order_total(order_prices)}")
print(f"إجمالي السلة الفارغة: {calculate_order_total([])}")
إجمالي الطلب: 80
إجمالي السلة الفارغة: 0

كل استدعاء للدالة يبدأ بقيمة محلية جديدة وواضحة، ولهذا لا يعتمد البرنامج على حالة مخفية أو متغير عالمي.

خطوات تشخيص الخطأ بسرعة

  1. اقرأ اسم المتغير داخل رسالة الخطأ.
  2. ابحث داخل الدالة عن أي سطر يسند قيمة لهذا الاسم، مثل = أو +=.
  3. تحقق هل استخدمت المتغير قبل سطر الإسناد.
  4. إذا كان المتغير يعرف داخل if أو try، افحص جميع مسارات التنفيذ.
  5. اسأل: هل أحتاج متغيرًا محليًا، أم أريد فعلًا تعديل متغير خارجي؟
  6. اختر الحل الأنسب: تهيئة محلية، تمرير وإرجاع قيمة، global، أو nonlocal.

الخلاصة

  • خطأ UnboundLocalError يعني أنك حاولت استخدام متغير محلي قبل أن يحصل على قيمة.
  • وجود إسناد إلى اسم داخل دالة يجعل Python تعتبره محليًا في الدالة كلها، حتى لو كان هناك متغير عام بالاسم نفسه.
  • هيّئ المتغيرات مثل total = 0 وcount = 0 قبل استخدام +=.
  • لا تعرف متغيرًا داخل شرط أو try فقط ثم تفترض أنه موجود في كل الحالات.
  • مرّر القيم إلى الدوال وأعد النتائج كلما كان ذلك ممكنًا؛ فهو أوضح من الاعتماد على global.
  • استخدم global لتعديل متغير عام عند الحاجة المقصودة، وnonlocal لتعديل متغير في دالة خارجية محيطة.
{alertSuccess} قاعدة ذهبية: قبل كتابة count += 1 أو total += value، تأكد أين تم إنشاء المتغير وبأي قيمة. في أغلب الدوال، ابدأ بقيمة محلية واضحة ثم أعد النتيجة.

مصادر خارجية رسمية للتوسع

أسئلة شائعة

ما معنى local variable referenced before assignment في Python؟

تعني أنك استخدمت متغيرًا اعتبرته Python محليًا داخل دالة، لكنك حاولت قراءته قبل إعطائه قيمة داخل الدالة.

لماذا يظهر UnboundLocalError رغم أن المتغير معرف خارج الدالة؟

لأن وجود عملية إسناد إلى الاسم نفسه داخل الدالة، مثل x = ... أو x += 1، يجعل Python تعتبره متغيرًا محليًا داخل تلك الدالة.

هل global هو الحل الأفضل دائمًا؟

لا. غالبًا يكون تمرير القيمة إلى الدالة ثم إرجاع القيمة الجديدة أوضح وأسهل في الصيانة. استخدم global فقط عندما تريد فعلًا تعديل متغير عام مشترك.

ما الفرق بين global وnonlocal؟

global يشير إلى متغير موجود في مستوى الملف خارج الدوال. أما nonlocal فيشير إلى متغير موجود داخل دالة خارجية تحيط بدالة متداخلة.

كيف أتجنب الخطأ داخل if أو try؟

هيئ المتغير بقيمة افتراضية قبل if أو try، أو أعد قيمة مناسبة من كل فرع ممكن حتى لا يبقى المتغير دون قيمة.

إرسال تعليق

أحدث أقدم