مشكلة وحل 26: حل خطأ ValueError too many values to unpack في بايثون

حل خطأ ValueError too many values to unpack في بايثون

قد تحاول توزيع عناصر قائمة أو Tuple على عدة متغيرات في بايثون، فتظهر لك رسالة الخطأ:

ValueError: too many values to unpack (expected 2)

تظهر هذه الرسالة كثيرًا عند استخدام أكثر من متغير في سطر واحد، أو المرور داخل حلقة for، أو التعامل مع القواميس، أو تقسيم نص باستخدام split(). وقد يكون الكود قريبًا جدًا من الصحيح، لكن عدد القيم التي تحاول توزيعها أكبر من عدد المتغيرات الموجودة لاستقبالها.

في هذا المقال من سلسلة مشكلة وحل ستفهم معنى Unpacking أولًا، ثم سنشرح أسباب الخطأ وحلوله في القوائم والقواميس والحلقات والدوال والملفات، مع توضيح الفرق بين too many values to unpack وnot enough values to unpack.

{alertInfo} الفكرة ببساطة: عند فك القيم يجب أن يساوي عدد المتغيرات عدد القيم. إذا كان لديك ثلاث قيم ومتغيران فقط، فلن تعرف بايثون أين تضع القيمة الثالثة، فيظهر خطأ too many values to unpack.

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

ما معنى too many values to unpack؟

تعني الرسالة أن بايثون وجدت قيمًا أكثر من عدد المتغيرات التي أعددتها لاستقبال هذه القيم.

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

name, age = ["Ali", 25, "Aden"]

في الجهة اليمنى توجد ثلاث قيم:

  • "Ali"
  • 25
  • "Aden"

أما في الجهة اليسرى فيوجد متغيران فقط:

  • name
  • age

تستطيع بايثون وضع أول قيمة داخل name والثانية داخل age، لكنها لا تجد متغيرًا يستقبل القيمة الثالثة، ولذلك يظهر الخطأ.

ما هو Unpacking في Python؟

Unpacking أو فك القيم هو توزيع عناصر كائن قابل للتكرار، مثل قائمة أو Tuple، على عدة متغيرات في سطر واحد.

مثال صحيح:

name, age, city = ["Ali", 25, "Aden"]

print(name)
print(age)
print(city)

الناتج:

Ali
25
Aden

هنا لدينا ثلاثة متغيرات وثلاث قيم، لذلك تم التوزيع بنجاح:

المتغير القيمة التي استقبلها
name "Ali"
age 25
city "Aden"
شرح فك القيم Unpacking وتوزيع عناصر القائمة على متغيرات في بايثون

أبسط حل: اجعل عدد المتغيرات مساويًا لعدد القيم

إذا كنت تحتاج إلى جميع القيم، أضف متغيرًا لكل قيمة.

الخطأ:

name, age = ["Ali", 25, "Aden"]

الحل:

name, age, city = ["Ali", 25, "Aden"]

لا يشترط أن تكون القيم داخل قائمة. تعمل الفكرة نفسها مع Tuple:

product, price, quantity = ("Keyboard", 35, 2)

print(product)
print(price)
print(quantity)

حل آخر: استخدم القيم التي تحتاجها فقط

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

data = ["Ali", 25, "Aden"]

name = data[0]
age = data[1]

print(name, age)

الناتج:

Ali 25

هذه الطريقة واضحة عندما تحتاج إلى عناصر محددة ولا تريد استقبال بقية القيم.

استخدام متغير مهمل للقيمة غير المطلوبة

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

name, age, _ = ["Ali", 25, "Aden"]

print(name)
print(age)

الشرطة السفلية _ ليست ميزة خاصة تمنع تخزين القيمة؛ هي متغير عادي، لكن المبرمجين يستخدمون هذا الاسم غالبًا للدلالة على أن القيمة لن تُستخدم.

{alertInfo} استخدم _ عندما تعرف عدد القيم وتريد تجاهل قيمة محددة. أما إذا كان عدد القيم الإضافية متغيرًا، فاستخدم النجمة *.

استخدام النجمة لاستقبال القيم الزائدة

تستطيع استخدام * قبل أحد المتغيرات ليجمع القيم الزائدة داخل قائمة.

name, age, *other_data = [
    "Ali",
    25,
    "Aden",
    "Developer"
]

print(name)
print(age)
print(other_data)

الناتج:

Ali
25
['Aden', 'Developer']

استقبل name القيمة الأولى، واستقبل age القيمة الثانية، وجمع other_data جميع القيم المتبقية داخل قائمة.

استقبال أول قيمة وآخر قيمة

يمكن وضع المتغير ذي النجمة في المنتصف، وبذلك تستقبل أول وآخر قيمة بشكل منفصل:

first, *middle, last = [10, 20, 30, 40, 50]

print("الأول:", first)
print("الوسط:", middle)
print("الأخير:", last)

الناتج:

الأول: 10
الوسط: [20, 30, 40]
الأخير: 50

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

هذا كود غير صحيح:

first, *middle, *rest = [1, 2, 3, 4]
استخدام النجمة starred expression لاستقبال القيم الزائدة في بايثون

الفرق بين too many values وnot enough values

يوجد خطآن متعاكسان عند فك القيم.

رسالة الخطأ المشكلة مثال
too many values to unpack عدد القيم أكبر من عدد المتغيرات. a, b = [1, 2, 3]
not enough values to unpack عدد القيم أقل من عدد المتغيرات. a, b, c = [1, 2]

مثال على القيم الزائدة:

a, b = [1, 2, 3]

ومثال على القيم غير الكافية:

a, b, c = [1, 2]

الحل في الحالتين يبدأ بعدّ القيم والمتغيرات ومعرفة شكل البيانات الحقيقي.

الفرق بين too many values وnot enough values to unpack في بايثون

ظهور الخطأ داخل for loop

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

مثال خاطئ:

students = [
    ("Ali", 90),
    ("Sara", 95, "A")
]

for name, score in students:
    print(name, score)

العنصر الأول يحتوي على قيمتين، لذلك ينجح. لكن العنصر الثاني يحتوي على ثلاث قيم، بينما الحلقة تنتظر متغيرين فقط.

الحل الأول: توحيد شكل البيانات

students = [
    ("Ali", 90),
    ("Sara", 95)
]

for name, score in students:
    print(name, score)

الحل الثاني: أضف متغيرًا ثالثًا

students = [
    ("Ali", 90, "B"),
    ("Sara", 95, "A")
]

for name, score, grade in students:
    print(name, score, grade)

الحل الثالث: اجمع القيم الإضافية

for name, score, *extra in students:
    print(name, score, extra)

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

الخطأ عند استخدام قائمة أسماء بدون enumerate

قد تكتب حلقة تتوقع رقم العنصر واسمه:

names = ["Ali", "Sara", "Omar"]

for index, name in names:
    print(index, name)

لكن كل عنصر داخل القائمة نص واحد، وليس زوجًا يحتوي على رقم واسم. وقد تحاول بايثون فك أحرف النص نفسه، فتظهر أخطاء مختلفة حسب طول الاسم.

الحل هو استخدام enumerate():

names = ["Ali", "Sara", "Omar"]

for index, name in enumerate(names, start=1):
    print(index, name)

الناتج:

1 Ali
2 Sara
3 Omar

الدالة enumerate() تعيد في كل دورة زوجًا يحتوي على رقم العنصر وقيمته، لذلك يناسبها متغيران.

الخطأ عند المرور على Dictionary

عند المرور مباشرة على قاموس، تعيد الحلقة المفاتيح فقط، وليس المفتاح والقيمة معًا.

مثال خاطئ:

user = {
    "name": "Ali",
    "age": 25
}

for key, value in user:
    print(key, value)

في كل دورة تعيد الحلقة مفتاحًا نصيًا مثل "name". تحاول بايثون فك حروف المفتاح إلى متغيرين، وقد يظهر too many values to unpack أو not enough values to unpack حسب طول المفتاح.

الحل هو استخدام items():

user = {
    "name": "Ali",
    "age": 25
}

for key, value in user.items():
    print(key, value)

الناتج:

name Ali
age 25

الدالة items() تعيد أزواجًا من المفتاح والقيمة، لذلك يمكن فك كل زوج داخل متغيرين.

حل خطأ فك القيم داخل for loop والقواميس باستخدام items في بايثون

الخطأ عند استخدام split

من أكثر الأسباب شيوعًا أن تتوقع كلمتين من split()، بينما يكتب المستخدم أكثر من كلمتين.

first_name, last_name = input("أدخل الاسم الكامل: ").split()

إذا كتب المستخدم:

Ali Ahmed

يعمل الكود لأن الناتج يحتوي على قيمتين. لكن إذا كتب:

Ali Ahmed Mohammed

تعيد split() ثلاث قيم، بينما يوجد متغيران فقط.

الحل باستخدام maxsplit

إذا كنت تريد تقسيم النص إلى جزأين فقط، استخدم maxsplit=1:

first_name, rest_of_name = input(
    "أدخل الاسم الكامل: "
).split(maxsplit=1)

إذا كتب المستخدم Ali Ahmed Mohammed تصبح القيم:

  • first_name = "Ali"
  • rest_of_name = "Ahmed Mohammed"

الحل باستخدام النجمة

first_name, *other_names = input(
    "أدخل الاسم الكامل: "
).split()

print(first_name)
print(other_names)

ستكون الأسماء الإضافية داخل قائمة.

ماذا لو كتب المستخدم كلمة واحدة فقط؟

استخدام maxsplit=1 لا يمنع خطأ not enough values to unpack إذا كتب المستخدم كلمة واحدة فقط.

لذلك يمكنك فحص الناتج قبل فك القيم:

parts = input("أدخل الاسم الكامل: ").split(maxsplit=1)

if len(parts) == 2:
    first_name, last_name = parts
    print(first_name, last_name)
else:
    print("اكتب الاسم الأول واسم العائلة.")

الخطأ عند استقبال قيم ترجعها دالة

قد تعدل دالة لتعيد ثلاث قيم، لكن تنسى تحديث السطر الذي يستقبل النتيجة.

def get_user():
    return "Ali", 25, "Aden"


name, age = get_user()

الدالة تعيد Tuple تحتوي على ثلاث قيم، لكن السطر يستخدم متغيرين.

الحل إذا كنت تحتاج جميع القيم:

name, age, city = get_user()

أو استقبل النتيجة كاملة داخل متغير واحد:

user_data = get_user()

print(user_data)
print(user_data[0])

الخطأ عند قراءة CSV أو بيانات خارجية

قد تتوقع أن كل صف يحتوي على ثلاثة أعمدة، لكن أحد الصفوف يحتوي على عمود رابع.

rows = [
    ["Ali", "25", "Aden"],
    ["Sara", "22", "Sanaa", "Developer"]
]

for name, age, city in rows:
    print(name, age, city)

أفضل حل ليس دائمًا إضافة النجمة فقط، بل فحص سبب اختلاف عدد الأعمدة. قد يكون الملف غير منظم أو الفاصل المستخدم في القراءة غير صحيح.

يمكنك فحص طول كل صف:

for row_number, row in enumerate(rows, start=1):
    if len(row) != 3:
        print(f"الصف {row_number} يحتوي على {len(row)} أعمدة.")
        continue

    name, age, city = row
    print(name, age, city)

الناتج:

Ali 25 Aden
الصف 2 يحتوي على 4 أعمدة.

فك القيم المتداخلة

إذا كانت البيانات متداخلة، يجب أن يعكس شكل المتغيرات شكل البيانات.

student = ("Ali", (90, 85))

name, (math_score, english_score) = student

print(name)
print(math_score)
print(english_score)

الناتج:

Ali
90
85

أما إذا كتبت ثلاثة متغيرات في المستوى الأول:

name, math_score, english_score = student

فلن يعمل، لأن المستوى الأول يحتوي على عنصرين فقط: الاسم وTuple الدرجات.

كيف تعرف شكل القيمة قبل فكها؟

عند عدم وضوح مصدر البيانات، اطبع القيمة ونوعها وطولها قبل سطر الخطأ:

result = get_user()

print("القيمة:", result)
print("النوع:", type(result))
print("عدد العناصر:", len(result))

قد ترى ناتجًا مثل:

القيمة: ('Ali', 25, 'Aden')
النوع: <class 'tuple'>
عدد العناصر: 3

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

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

  1. انتقل إلى آخر سطر من رسالة الخطأ داخل ملفك.
  2. ابحث عن عملية فك قيم مثل a, b = value أو for a, b in items.
  3. اطبع القيمة الموجودة في الجهة اليمنى.
  4. استخدم type() لمعرفة نوعها.
  5. استخدم len() إذا كانت تدعم الطول.
  6. قارن عدد العناصر بعدد المتغيرات.
  7. قرر هل تحتاج جميع القيم، أم جزءًا منها، أم تريد جمع القيم الزائدة.
  8. راجع مصدر البيانات إذا كان عدد العناصر يفترض أن يكون ثابتًا.

جدول سريع لاختيار الحل المناسب

الحالة الحل المناسب
عدد القيم ثابت وتحتاجها كلها استخدم متغيرًا لكل قيمة.
تحتاج أول قيمتين فقط استخدم الفهرسة أو slicing.
تريد تجاهل قيمة واحدة معروفة استخدم المتغير _.
عدد القيم الإضافية غير ثابت استخدم متغيرًا يحمل النجمة مثل *rest.
تريد رقم العنصر وقيمته داخل حلقة استخدم enumerate().
تريد مفتاح القاموس وقيمته استخدم dictionary.items().
النص قد يحتوي على كلمات كثيرة استخدم split(maxsplit=...) أو النجمة.
صفوف الملف غير متساوية افحص طول الصفوف وأصلح مصدر البيانات.

مثال عملي: قراءة بيانات جهات اتصال

لدينا أسطر تحتوي على الاسم ورقم الهاتف والبريد، ويفصل بينها الرمز |.

contact_lines = [
    "Ali|777123456|ali@example.com",
    "Sara|733987654|sara@example.com"
]

for line in contact_lines:
    name, phone, email = line.split("|")

    print("الاسم:", name)
    print("الهاتف:", phone)
    print("البريد:", email)

هذا يعمل ما دام كل سطر يحتوي على ثلاثة أجزاء. لكن قد يظهر سطر يحتوي على ملاحظة إضافية:

Ali|777123456|ali@example.com|Friend

إذا كانت الملاحظة اختيارية، يمكن استخدام النجمة:

for line in contact_lines:
    name, phone, email, *notes = line.split("|")

    print(name, phone, email, notes)

أما إذا كان الملف يجب أن يحتوي على ثلاثة أعمدة فقط، فالأفضل اعتبار السطر غير صالح بدل إخفاء المشكلة:

for line_number, line in enumerate(contact_lines, start=1):
    parts = line.split("|")

    if len(parts) != 3:
        print(f"السطر {line_number} غير صالح: {line}")
        continue

    name, phone, email = parts
    print(name, phone, email)

أخطاء شائعة عند حل المشكلة

1. إضافة متغيرات دون فهم البيانات

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

2. استخدام النجمة في كل مكان

النجمة مفيدة، لكنها قد تخفي اختلافًا غير مقصود في بنية البيانات. استخدمها عندما تكون القيم الإضافية جزءًا طبيعيًا من التصميم.

3. الخلط بين المرور على القاموس وitems

المرور على القاموس مباشرة يعيد المفاتيح، بينما items() تعيد أزواج المفتاح والقيمة.

4. افتراض أن إدخال المستخدم ثابت

المستخدم قد يكتب اسمًا من كلمة أو ثلاث كلمات. لا تفترض دائمًا أن split() ستعيد جزأين بالضبط.

5. تجاهل الصف الذي سبب المشكلة

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

6. استخدام except ValueError دون تشخيص السبب

يمكنك التقاط الخطأ، لكن ذلك لا يصلح بنية البيانات تلقائيًا:

try:
    name, age = data
except ValueError:
    print("عدد القيم لا يطابق عدد المتغيرات.")

هذا يمنع توقف البرنامج، لكنه يجب أن يتبعه قرار واضح: هل ستطلب الإدخال مرة أخرى، أم تتجاهل السطر، أم تصلح مصدر البيانات؟

تمرين عملي

لديك قائمة منتجات، لكن بعض المنتجات تحتوي على وصف إضافي:

products = [
    ("Keyboard", 35),
    ("Mouse", 18, "Wireless"),
    ("Monitor", 160, "27 inch", "4K")
]

اكتب حلقة تطبع اسم المنتج وسعره، وتجمع أي معلومات إضافية داخل قائمة.

الحل

for name, price, *details in products:
    print("المنتج:", name)
    print("السعر:", price)
    print("تفاصيل إضافية:", details)
    print("-" * 30)

الناتج:

المنتج: Keyboard
السعر: 35
تفاصيل إضافية: []
------------------------------
المنتج: Mouse
السعر: 18
تفاصيل إضافية: ['Wireless']
------------------------------
المنتج: Monitor
السعر: 160
تفاصيل إضافية: ['27 inch', '4K']
------------------------------

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

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

الخلاصة

يظهر خطأ ValueError: too many values to unpack عندما تحاول توزيع قيم أكثر من عدد المتغيرات الموجودة. أبسط مثال هو محاولة وضع ثلاث قيم داخل متغيرين.

تعلمنا أن الحل قد يكون زيادة عدد المتغيرات، أو استخدام الفهرسة، أو تجاهل قيمة باستخدام _، أو جمع القيم الزائدة داخل قائمة باستخدام متغير يحمل النجمة.

كما شرحنا أسباب ظهور الخطأ داخل for loop، وعند استخدام القواميس بدون items()، وعند تقسيم النص باستخدام split()، وعند استقبال نتائج الدوال أو قراءة صفوف غير متساوية من الملفات.

{alertSuccess} القاعدة المهمة: لا تصلح الخطأ بالتخمين. اطبع القيمة التي تحاول فكها، واعرف نوعها وعدد عناصرها، ثم اجعل شكل المتغيرات مطابقًا لشكل البيانات الحقيقي.

أسئلة شائعة

ما معنى too many values to unpack expected 2؟

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

كيف أحل ValueError too many values to unpack؟

اجعل عدد المتغيرات مساويًا لعدد القيم، أو استخدم متغيرًا يحمل النجمة لجمع القيم الزائدة، أو استخدم الفهرسة إذا كنت تحتاج إلى عناصر محددة فقط.

ما الفرق بين too many values وnot enough values؟

too many values تعني أن القيم أكثر من المتغيرات، بينما not enough values تعني أن القيم أقل من المتغيرات.

لماذا يظهر الخطأ داخل for loop؟

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

لماذا أستخدم items مع Dictionary؟

لأن المرور على القاموس مباشرة يعيد المفاتيح فقط، بينما items() تعيد في كل دورة زوجًا يحتوي على المفتاح والقيمة.

كيف أحصل على رقم العنصر وقيمته داخل الحلقة؟

استخدم enumerate()، مثل for index, value in enumerate(items).

كيف أتعامل مع عدد غير معروف من القيم؟

استخدم متغيرًا يحمل النجمة، مثل first, *rest = values، حيث تجمع rest جميع القيم الإضافية داخل قائمة.

كيف أمنع الخطأ عند استخدام split؟

استخدم maxsplit إذا كنت تريد عددًا محددًا من الأجزاء، أو افحص طول القائمة الناتجة قبل فكها، أو استخدم النجمة لاستقبال الكلمات الإضافية.

هل استخدام try except هو الحل الأفضل؟

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

إرسال تعليق

أحدث أقدم