قد تحاول توزيع عناصر قائمة أو 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"
أما في الجهة اليسرى فيوجد متغيران فقط:
nameage
تستطيع بايثون وضع أول قيمة داخل 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" |
أبسط حل: اجعل عدد المتغيرات مساويًا لعدد القيم
إذا كنت تحتاج إلى جميع القيم، أضف متغيرًا لكل قيمة.
الخطأ:
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]
الفرق بين 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]
الحل في الحالتين يبدأ بعدّ القيم والمتغيرات ومعرفة شكل البيانات الحقيقي.
ظهور الخطأ داخل 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() تعيد أزواجًا من المفتاح والقيمة، لذلك يمكن فك كل زوج داخل متغيرين.
الخطأ عند استخدام 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
بعد ذلك يصبح واضحًا أنك تحتاج إلى ثلاثة متغيرات، أو متغير واحد يستقبل النتيجة كاملة، أو نجمة تجمع القيم الإضافية.
خطوات تشخيص الخطأ بسرعة
- انتقل إلى آخر سطر من رسالة الخطأ داخل ملفك.
- ابحث عن عملية فك قيم مثل
a, b = valueأوfor a, b in items. - اطبع القيمة الموجودة في الجهة اليمنى.
- استخدم
type()لمعرفة نوعها. - استخدم
len()إذا كانت تدعم الطول. - قارن عدد العناصر بعدد المتغيرات.
- قرر هل تحتاج جميع القيم، أم جزءًا منها، أم تريد جمع القيم الزائدة.
- راجع مصدر البيانات إذا كان عدد العناصر يفترض أن يكون ثابتًا.
جدول سريع لاختيار الحل المناسب
| الحالة | الحل المناسب |
|---|---|
| عدد القيم ثابت وتحتاجها كلها | استخدم متغيرًا لكل قيمة. |
| تحتاج أول قيمتين فقط | استخدم الفهرسة أو 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']
------------------------------
روابط داخلية مفيدة من بايثون العرب
- سلسلة مشكلة وحل في بايثون
- مشكلة وحل 25: حل خطأ ZeroDivisionError في بايثون
- حل خطأ TypeError unsupported operand type(s)
- حل خطأ ValueError عند تحويل النص إلى رقم
- شرح Dictionary في Python للمبتدئين
- شرح Lists في Python للمبتدئين
- شرح for loop في Python للمبتدئين
- شرح أخطاء Python وطريقة قراءة Traceback
مصادر خارجية رسمية للتوسع
- هياكل البيانات في توثيق Python الرسمي
- أدوات التحكم والحلقات في Python
- توثيق enumerate الرسمي
- توثيق dict.items الرسمي
- جمل الإسناد وفك القيم في Python
الخلاصة
يظهر خطأ 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 هو الحل الأفضل؟
يمكنه منع توقف البرنامج، لكنه لا يصلح اختلاف بنية البيانات. الأفضل فهم سبب اختلاف عدد القيم ثم معالجة المصدر أو المدخل بطريقة واضحة.



