قد تعرّف متغيرًا خارج دالة في Python، ثم تستدعيه داخل الدالة، فتظهر لك رسالة مثل:
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 داخل الدالة، فتتعامل مع الاسم كمتغير محلي في كامل الدالة. وعند محاولة قراءته قبل أن يحصل على قيمة محلية، يظهر الخطأ.
{alertInfo} الفكرة ببساطة: إذا عدّلت اسم متغير داخل دالة، تعتبره Python متغيرًا محليًا تلقائيًا ما لم تعلن بوضوح أنهglobalأوnonlocal. لذلك لا يمكنك قراءته قبل أن يحصل على قيمة داخل الدالة.
{getToc} $title={محتوى المقال}
ما معنى UnboundLocalError في Python؟
UnboundLocalError هو نوع خاص من NameError. يظهر عندما تحاول دالة استخدام متغير محلي قبل أن ترتبط به قيمة. بمعنى أبسط: Python تعرف أن الاسم تابع للدالة الحالية، لكنها لم تجد له قيمة وقت استخدامه.
| جزء الرسالة | معناه |
|---|---|
UnboundLocalError | المتغير اعتُبر محليًا داخل الدالة، لكنه لم يحصل على قيمة محلية بعد. |
local variable | Python تتعامل مع الاسم كمتغير تابع للدالة الحالية. |
referenced before assignment | تمت قراءة المتغير قبل تنفيذ سطر يعطيه قيمة. |
أبسط مثال يسبب الخطأ
score = 10
def increase_score():
print(score)
score = score + 1
increase_score()
قد تتوقع أن يطبع الكود 10 ثم يزيد القيمة، لكنه سيظهر خطأ. السبب أن السطر التالي يحتوي على إسناد إلى score داخل الدالة:
score = score + 1
لذلك Python تقرر أن score محلي في هذه الدالة كلها. وعندما تصل إلى print(score)، تحاول قراءة المتغير المحلي قبل أن يحصل على قيمة.
ما هو Scope أو نطاق المتغير؟
نطاق المتغير scope يحدد المكان الذي يمكن الوصول إلى المتغير منه. لفهم هذا الخطأ نحتاج إلى معرفة ثلاثة نطاقات:
| النوع | أين يُعرّف؟ | مثال |
|---|---|---|
متغير عام global | خارج الدوال في مستوى الملف. | score = 10 |
متغير محلي local | داخل دالة. | name = "Ali" داخل def. |
متغير من نطاق خارجي nonlocal | داخل دالة متداخلة، ويخص الدالة المحيطة. | متغير داخل outer() تستخدمه inner(). |
يمكنك قراءة متغير عام داخل دالة ما دمت لا تعيد إسناد قيمة إليه داخلها. أما إذا عدلته أو كتبت += عليه، يجب أن تحدد بوضوح ما إذا كنت تريد متغيرًا محليًا أم متغيرًا خارجيًا.
لماذا لا يظهر الخطأ عند القراءة فقط؟
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 قبل تهيئة المتغير داخل النطاق المناسب.
هل أستخدم 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.
الفرق بين NameError وUnboundLocalError
| الخطأ | متى يظهر؟ | مثال |
|---|---|---|
NameError | عندما لا تجد Python الاسم في النطاقات المتاحة أصلًا. | print(user_name) دون تعريف user_name. |
UnboundLocalError | عندما يكون الاسم محليًا داخل الدالة، لكنك استخدمته قبل إعطائه قيمة. | count += 1 قبل تهيئة count. |
بمعنى آخر: UnboundLocalError هو حالة أكثر تحديدًا من NameError.
أخطاء شائعة تسبب 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
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
3. استخدام += أو -= قبل القيمة الأولية
لا تكتب count += 1 أو total += price إلا بعد تهيئة المتغير داخل النطاق الصحيح.
4. توقع أن if أو try تعمل دائمًا
إذا عرفت متغيرًا في مسار واحد فقط من التنفيذ، اسأل: ماذا يحدث في المسار الآخر؟ ضع قيمة افتراضية أو أعد نتيجة واضحة من كل فرع.
مثال عملي كامل: حساب إجمالي طلب بأمان
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
كل استدعاء للدالة يبدأ بقيمة محلية جديدة وواضحة، ولهذا لا يعتمد البرنامج على حالة مخفية أو متغير عالمي.
خطوات تشخيص الخطأ بسرعة
- اقرأ اسم المتغير داخل رسالة الخطأ.
- ابحث داخل الدالة عن أي سطر يسند قيمة لهذا الاسم، مثل
=أو+=. - تحقق هل استخدمت المتغير قبل سطر الإسناد.
- إذا كان المتغير يعرف داخل
ifأوtry، افحص جميع مسارات التنفيذ. - اسأل: هل أحتاج متغيرًا محليًا، أم أريد فعلًا تعديل متغير خارجي؟
- اختر الحل الأنسب: تهيئة محلية، تمرير وإرجاع قيمة،
global، أوnonlocal.
روابط داخلية مفيدة من بايثون العرب
- مشكلة وحل 9: حل خطأ NameError name is not defined في بايثون
- أساسيات بايثون 31: شرح try و except في Python للمبتدئين
- أساسيات بايثون 34: شرح class وobject في Python للمبتدئين
- سلسلة مشكلة وحل في بايثون
الخلاصة
- خطأ
UnboundLocalErrorيعني أنك حاولت استخدام متغير محلي قبل أن يحصل على قيمة. - وجود إسناد إلى اسم داخل دالة يجعل Python تعتبره محليًا في الدالة كلها، حتى لو كان هناك متغير عام بالاسم نفسه.
- هيّئ المتغيرات مثل
total = 0وcount = 0قبل استخدام+=. - لا تعرف متغيرًا داخل شرط أو
tryفقط ثم تفترض أنه موجود في كل الحالات. - مرّر القيم إلى الدوال وأعد النتائج كلما كان ذلك ممكنًا؛ فهو أوضح من الاعتماد على
global. - استخدم
globalلتعديل متغير عام عند الحاجة المقصودة، وnonlocalلتعديل متغير في دالة خارجية محيطة.
{alertSuccess} قاعدة ذهبية: قبل كتابةcount += 1أوtotal += value، تأكد أين تم إنشاء المتغير وبأي قيمة. في أغلب الدوال، ابدأ بقيمة محلية واضحة ثم أعد النتيجة.
مصادر خارجية رسمية للتوسع
- UnboundLocalError — Python Built-in Exceptions
- Execution model and name resolution — Python Documentation
- Python FAQ: Why am I getting an UnboundLocalError?
أسئلة شائعة
ما معنى local variable referenced before assignment في Python؟
تعني أنك استخدمت متغيرًا اعتبرته Python محليًا داخل دالة، لكنك حاولت قراءته قبل إعطائه قيمة داخل الدالة.
لماذا يظهر UnboundLocalError رغم أن المتغير معرف خارج الدالة؟
لأن وجود عملية إسناد إلى الاسم نفسه داخل الدالة، مثل x = ... أو x += 1، يجعل Python تعتبره متغيرًا محليًا داخل تلك الدالة.
هل global هو الحل الأفضل دائمًا؟
لا. غالبًا يكون تمرير القيمة إلى الدالة ثم إرجاع القيمة الجديدة أوضح وأسهل في الصيانة. استخدم global فقط عندما تريد فعلًا تعديل متغير عام مشترك.
ما الفرق بين global وnonlocal؟
global يشير إلى متغير موجود في مستوى الملف خارج الدوال. أما nonlocal فيشير إلى متغير موجود داخل دالة خارجية تحيط بدالة متداخلة.
كيف أتجنب الخطأ داخل if أو try؟
هيئ المتغير بقيمة افتراضية قبل if أو try، أو أعد قيمة مناسبة من كل فرع ممكن حتى لا يبقى المتغير دون قيمة.



