مشكلة وحل #1: الخطأ 'NoneType' object is not subscriptable

من الأخطاء التي تربك المبتدئين في بايثون خطأ: TypeError: 'NoneType' object is not subscriptable خصوصًا عندما يظهر أثناء التعامل مع القوائم أو القواميس أو نتائج الدوال.

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

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

{alertInfo} هذا الخطأ يعني غالبًا أنك تحاول استخدام الأقواس المربعة [] على قيمة فارغة من النوع None، بينما الفهرسة تعمل مع كائنات مثل list و string و dictionary.

نص المشكلة

تخيل أن مبتدئًا في بايثون كتب برنامجًا يحاول استخراج قيمة من قاموس أو نتيجة دالة، ثم ظهر له الخطأ التالي:

TypeError: 'NoneType' object is not subscriptable

قد يبدو السطر الذي سبب المشكلة صحيحًا من ناحية الشكل، مثل:

print(data["name"])

لكن المشكلة ليست دائمًا في الأقواس أو في اسم المفتاح، بل في قيمة المتغير نفسه. قد تكون قيمة data هي None وليس قاموسًا كما تتوقع.

الحل المختصر

الحل المباشر هو أن تتحقق من أن المتغير ليس None قبل محاولة الوصول إلى عنصر داخله باستخدام الأقواس المربعة.

بدلًا من كتابة هذا الكود مباشرة:

data = get_data()

print(data["name"])

اكتب شرطًا بسيطًا قبل استخدام الفهرسة:

data = get_data()

if data is not None:
    print(data["name"])
else:
    print("لم يتم العثور على بيانات.")
{alertSuccess} الفكرة الأساسية: قبل أن تستخدم [] تأكد أن المتغير يحتوي فعلًا على قائمة أو قاموس أو نص، وليس None.

ما معنى NoneType object is not subscriptable؟

لنفهم الرسالة جزءًا جزءًا:

جزء الخطأ المعنى
TypeError يعني أنك تحاول تنفيذ عملية غير مناسبة على نوع بيانات معيّن.
NoneType يعني أن الكائن الذي تتعامل معه قيمته None.
object is not subscriptable يعني أن هذا الكائن لا يمكن استخدام الأقواس المربعة [] معه.

بمعنى أبسط: أنت تطلب من بايثون أن يعطيك عنصرًا داخل شيء فارغ أصلًا.

ما المقصود بـ subscriptable في بايثون؟

الكائن القابل للفهرسة هو الكائن الذي يمكنك الوصول إلى عناصره باستخدام الأقواس المربعة [].

أمثلة على كائنات تدعم الفهرسة:

names = ["Ali", "Omar", "Sara"]
print(names[0])

text = "Python"
print(text[0])

student = {"name": "Ali"}
print(student["name"])

أما None فلا يحتوي على عناصر، لذلك لا يمكن كتابة:

data = None

print(data["name"])

وهنا سيظهر الخطأ:

TypeError: 'NoneType' object is not subscriptable

مثال عملي يوضح سبب الخطأ

لنفترض أن لدينا دالة تبحث عن طالب حسب الرقم الجامعي. إذا وجدت الطالب ترجع قاموسًا، وإذا لم تجده ترجع None.

def find_student(student_id):
    if student_id == 123:
        return {
            "name": "Ahmed",
            "grade": "Very Good"
        }

    return None


student = find_student(999)

print(student["name"])

في المثال السابق، الرقم 999 غير موجود، لذلك الدالة سترجع None. بعد ذلك حاولنا الوصول إلى:

student["name"]

لكن student ليس قاموسًا، بل قيمته None، لذلك يظهر الخطأ.

الحل الصحيح للمثال السابق

نضيف شرطًا قبل الوصول إلى قيمة name:

def find_student(student_id):
    if student_id == 123:
        return {
            "name": "Ahmed",
            "grade": "Very Good"
        }

    return None


student = find_student(999)

if student is not None:
    print(student["name"])
else:
    print("لم يتم العثور على الطالب.")

بهذا الشكل لن ينهار البرنامج، وسيعرض رسالة واضحة عندما لا توجد بيانات.

أسباب ظهور هذا الخطأ في بايثون

هذا الخطأ قد يظهر في أكثر من حالة، وأشهر أسبابه هي:

  • دالة ترجع None بدلًا من قاموس أو قائمة.
  • نسيان كتابة return داخل الدالة.
  • محاولة استخدام نتيجة دالة لا ترجع قيمة.
  • استخدام دوال تعدل الكائن في مكانه وترجع None مثل list.sort().
  • البحث عن عنصر غير موجود ثم استخدام النتيجة مباشرة بدون تحقق.

مثال شائع: نسيان return داخل الدالة

إذا كتبت دالة ولم تضع فيها return، فإن بايثون ترجع None تلقائيًا.

def get_user():
    user = {"name": "Ali"}
    # نسينا return هنا


result = get_user()

print(result["name"])

الكود السابق سيؤدي إلى نفس الخطأ؛ لأن result أصبح None.

التصحيح:

def get_user():
    user = {"name": "Ali"}
    return user


result = get_user()

print(result["name"])
{alertWarning} إذا كانت الدالة يجب أن تعطيك نتيجة، فتأكد أنها تحتوي على return في جميع الحالات المناسبة.

مثال شائع: استخدام list.sort بطريقة خاطئة

من الأخطاء الشائعة أن يكتب المبتدئ:

numbers = [3, 1, 2]

sorted_numbers = numbers.sort()

print(sorted_numbers[0])

المشكلة أن sort() ترتب القائمة الأصلية في مكانها، لكنها لا ترجع قائمة جديدة، بل ترجع None.

الحل الأول:

numbers = [3, 1, 2]

numbers.sort()

print(numbers[0])

أو استخدم sorted() إذا كنت تريد قائمة جديدة:

numbers = [3, 1, 2]

sorted_numbers = sorted(numbers)

print(sorted_numbers[0])

استخدام get بطريقة آمنة مع القواميس

إذا كنت تتعامل مع قاموس وقد لا تكون البيانات موجودة، يمكنك استخدام get() بطريقة آمنة:

student = None

name = (student or {}).get("name", "غير معروف")

print(name)

هنا استخدمنا:

student or {}

حتى إذا كانت قيمة student هي None، يتم استخدام قاموس فارغ بدلًا منها.

هل try-except حل مناسب؟

يمكن استخدام try-except لمنع توقف البرنامج، لكنه ليس دائمًا أفضل حل للمبتدئ. الأفضل غالبًا أن تفهم لماذا أصبحت القيمة None وتصلح السبب من المصدر.

مثال:

try:
    print(student["name"])
except TypeError:
    print("البيانات غير موجودة أو فارغة.")
{alertInfo} استخدم try-except عند الحاجة، لكن لا تجعله يخفي المشكلة الأساسية. الأفضل أن تعرف لماذا المتغير أصبح None.

كيف تعرف المتغير الذي قيمته None؟

أسهل طريقة للمبتدئ هي طباعة قيمة المتغير قبل استخدامه:

print(student)
print(type(student))

إذا كانت النتيجة:

None
<class 'NoneType'>

فهذا يعني أن المشكلة في قيمة المتغير قبل سطر الفهرسة، وليس في الأقواس نفسها.

ملخص سريع للحل

السبب الحل
المتغير قيمته None تحقق منه باستخدام if value is not None.
الدالة لا ترجع قيمة أضف return مناسبًا داخل الدالة.
استخدام sort بطريقة خاطئة استخدم list.sort() بدون تخزين النتيجة، أو استخدم sorted().
البيانات قد تكون غير موجودة استخدم get() أو قيمة افتراضية.

روابط مفيدة من بايثون العرب

الخلاصة

خطأ NoneType object is not subscriptable يحدث عندما تحاول استخدام الأقواس المربعة [] على قيمة None. الحل ليس حذف الأقواس مباشرة، بل معرفة لماذا أصبح المتغير None من الأساس.

قبل الوصول إلى عناصر القوائم أو القواميس، تحقق من قيمة المتغير، واستخدم return بشكل صحيح داخل الدوال، وانتبه للدوال التي تعدل الكائن في مكانه مثل sort().

{alertSuccess} القاعدة الذهبية: إذا ظهر لك هذا الخطأ، اطبع قيمة المتغير ونوعه قبل سطر الخطأ، وستعرف بسرعة هل تتعامل مع None بدلًا من قائمة أو قاموس.

إرسال تعليق

أحدث أقدم