إذا حاولت استخدام قائمة داخل Dictionary أو Set ثم ظهرت الرسالة TypeError: unhashable type: 'list'، فالمشكلة ليست في شكل الأقواس ولا في قيمة عناصر القائمة. المعنى ببساطة أن بايثون احتاجت في هذا الموضع إلى كائن Hashable، لكنك مررت List، والقوائم في بايثون ليست Hashable.
يظهر الخطأ غالبًا عند استخدام List كمفتاح Dictionary، أو إضافتها كعنصر داخل Set، أو عند تمرير List of Lists مباشرة إلى set() لإزالة التكرار. وقد يظهر أيضًا عندما تستخدم Tuple كمفتاح لكنها تحتوي داخلها List.
في مشكلة وحل 29 سنفهم معنى Hashable وUnhashable، ولماذا تنجح Tuple في بعض الحالات، وكيف نحذف التكرار من List of Lists، ومتى تكون frozenset أنسب من Tuple، وكيف تختار الحل الصحيح بدل تحويل البيانات عشوائيًا حتى يختفي الخطأ.
{alertInfo}
الحل المختصر: مفاتيح Dictionary وعناصر Set يجب أن تكون Hashable. إذا كانت List تمثل قيمة ثابتة ومرتبة منطقيًا، يمكن أن تكون Tuple بديلًا مناسبًا. وإذا كانت تمثل مجموعة لا يهم ترتيبها، فقد تكون frozenset أفضل.
{getToc} $title={محتوى المقال}
ما معنى unhashable type: list؟
الدالة hash() تسمح لنا باختبار بعض الكائنات Hashable:
print(hash("Ali"))
print(hash(100))
print(hash((10, 20)))
أما List:
print(hash([10, 20]))
فتؤدي إلى:
TypeError: unhashable type: 'list'
الكائن Hashable يجب أن يوفر Hash متوافقًا مع المساواة. Dictionary وSet تعتمدان على هذا المفهوم لتنظيم المفاتيح والعناصر والبحث عنها بكفاءة.
لماذا List ليست Hashable؟
List قابلة للتعديل بعد إنشائها:
numbers = [10, 20]
numbers.append(30)
numbers[0] = 99
لهذا لا تستخدم القوائم المدمجة في بايثون كمفاتيح Hash ثابتة. وهي لا توفر __hash__ مناسبًا للاستخدام كمفتاح Dictionary أو عنصر Set.
{alertWarning} انتبه: عبارة «كل Immutable هو Hashable» ليست قاعدة دقيقة لكل الحالات. Tuple مثلًا غير قابلة للتعديل، لكنها تصبح غير Hashable إذا احتوت عنصرًا غير Hashable مثل List.
الحالة الأولى: List كمفتاح Dictionary
الكود التالي خاطئ:
locations = {
[10, 20]: "Aden"
}
المفتاح هنا List. إذا كانت القيمتان تمثلان إحداثيًا ثابتًا ومرتبًا، استخدم Tuple:
locations = {
(10, 20): "Aden"
}
print(locations[(10, 20)])
لماذا Tuple تعمل هنا؟
Tuple نفسها غير قابلة للتعديل، وإذا كانت جميع عناصرها Hashable فإن Tuple تكون Hashable ويمكن استخدامها كمفتاح.
key = (10, 20, "A")
print(hash(key))
لكن ليست كل Tuple Hashable
هذا المثال يفشل:
key = (
10,
[20, 30]
)
print(hash(key))
السبب أن Tuple تحتوي List غير Hashable. وجود حاوية خارجية من نوع Tuple لا يحول العناصر الداخلية إلى Hashable.
كيف أصلح Tuple تحتوي List؟
إذا كانت القائمة الداخلية تمثل بيانات ثابتة ومرتبة:
key = (
10,
(20, 30)
)
print(hash(key))
الحالة الثانية: إضافة List إلى Set
هذا لا يعمل:
items = set()
items.add([1, 2])
عناصر Set يجب أن تكون Hashable. إذا كان الزوج ثابتًا ومرتبًا:
items = set()
items.add((1, 2))
print(items)
الحالة الثالثة: set مع List of Lists
كثير من المستخدمين يحاولون إزالة التكرار هكذا:
data = [
[1, 2],
[1, 2],
[3, 4]
]
unique = set(data)
لكن كل عنصر داخل data هو List، ولذلك يظهر الخطأ.
الحل باستخدام Tuple
unique = {
tuple(item)
for item in data
}
print(unique)
نتيجة ممكنة:
{(1, 2), (3, 4)}
لا تعتمد على ترتيب Set عند العرض.
إعادة النتيجة إلى Lists
unique_lists = [
list(item)
for item in unique
]
print(unique_lists)
إزالة التكرار مع الحفاظ على ترتيب أول ظهور
إذا كان الترتيب مهمًا، استخدم Set للتتبع وList للنتيجة:
data = [
[1, 2],
[1, 2],
[3, 4],
[5, 6],
[3, 4]
]
seen = set()
unique = []
for item in data:
key = tuple(item)
if key not in seen:
seen.add(key)
unique.append(item)
print(unique)
الناتج:
[[1, 2], [3, 4], [5, 6]]
ماذا لو كانت القوائم متداخلة؟
هذا التحويل لا يكفي:
item = [
1,
[2, 3]
]
key = tuple(item)
print(hash(key))
لأن List الداخلية ما زالت موجودة. إذا كانت البنية ثابتة ويمكن تمثيلها منطقيًا كTuple متداخلة:
key = (
1,
(2, 3)
)
print(hash(key))
أما بيانات JSON المعقدة فلا تحولها آليًا إلى Tuples دون فهم بنيتها ومعنى المفتاح الذي تحتاجه.
أشهر الأنواع Hashable وUnhashable
| النوع | Hashable عادة؟ | يصلح Dictionary Key؟ |
|---|---|---|
int | نعم | نعم |
float | نعم | نعم |
str | نعم | نعم |
bytes | نعم | نعم |
tuple | إذا كانت عناصرها Hashable | نعم عند تحقق الشرط |
frozenset | إذا كانت عناصرها Hashable | نعم |
list | لا | لا |
dict | لا | لا |
set | لا | لا |
كيف أختبر Hashability؟
def is_hashable(value):
try:
hash(value)
return True
except TypeError:
return False
print(is_hashable("Ali"))
print(is_hashable([1, 2]))
print(is_hashable((1, 2)))
الناتج:
True
False
True
استخدام collections.abc.Hashable
from collections.abc import Hashable
print(isinstance("Ali", Hashable))
print(isinstance([1, 2], Hashable))
ما الفرق بين tuple وfrozenset؟
Tuple تمثل ترتيبًا:
print(
(1, 2) == (2, 1)
)
الناتج False.
أما frozenset فتمثل مجموعة لا يهم ترتيب عناصرها:
print(
frozenset({1, 2})
==
frozenset({2, 1})
)
الناتج True.
متى أستخدم frozenset كمفتاح؟
إذا كان ترتيب القيم لا يحمل معنى. مثل مجموعة صلاحيات:
permissions = {
frozenset({"read"}): "viewer",
frozenset({"read", "write"}): "editor"
}
key = frozenset(
{"write", "read"}
)
print(permissions[key])
الناتج:
editor
لماذا set نفسها لا تصلح كمفتاح؟
print(
hash({"read", "write"})
)
ستظهر:
TypeError: unhashable type: 'set'
set قابلة للتعديل، بينما frozenset غير قابلة للتعديل ويمكن أن تكون Hashable.
هل تحويل List إلى Tuple هو الحل دائمًا؟
لا. هذه أهم نقطة عملية في المقال. إذا كانت List تمثل بيانات متغيرة، فإن تحويلها إلى Tuple لمجرد استخدامها كمفتاح قد يخفي تصميمًا سيئًا.
مثل سجل مستخدم يحتوي الاسم والمدينة والعمر والحالة. بدل جعل السجل كله مفتاحًا، استخدم ID:
users = {
1001: {
"name": "Ali",
"city": "Aden",
"age": 25,
"status": "active"
}
}
المفتاح هنا واضح وثابت، بينما بيانات المستخدم يمكن تعديلها داخل القيمة.
مفتاح مركب باستخدام Tuple
Tuple ممتازة إذا كان المفتاح يتكون منطقيًا من أكثر من قيمة ثابتة، مثل الفرع والمنتج:
inventory = {
("Aden", "P001"): 20,
("Aden", "P002"): 15,
("Sanaa", "P001"): 8
}
print(
inventory[("Aden", "P001")]
)
List داخل مفتاح مركب
هذا يفشل إذا كانت roles List:
key = (
user_id,
roles
)
إذا كان ترتيب الأدوار مهمًا:
key = (
user_id,
tuple(roles)
)
وإذا كان ترتيبها غير مهم:
key = (
user_id,
frozenset(roles)
)
List of Dictionaries وإزالة التكرار
هذا لا يعمل:
users = [
{"id": 1, "name": "Ali"},
{"id": 1, "name": "Ali"},
{"id": 2, "name": "Sara"}
]
unique = set(users)
لأن Dictionary أيضًا غير Hashable.
الحل حسب ID
seen_ids = set()
unique_users = []
for user in users:
user_id = user["id"]
if user_id not in seen_ids:
seen_ids.add(user_id)
unique_users.append(user)
print(unique_users)
هذا أفضل من محاولة جعل Dictionary كاملة Hashable عندما يكون لديك مفتاح منطقي جاهز.
خطأ unhashable type list مع lru_cache
المفهوم يظهر أيضًا مع Cache، لأن Arguments المستخدمة لتكوين مفتاح Cache يجب أن تكون Hashable.
from functools import lru_cache
@lru_cache
def total(values):
return sum(values)
total([1, 2, 3])
يمكن استخدام Tuple إذا كانت القيم تمثل مدخلًا ثابتًا:
@lru_cache
def total(values):
return sum(values)
print(
total((1, 2, 3))
)
واجهة تستقبل List ثم تحولها داخليًا
from functools import lru_cache
@lru_cache
def _total_cached(values):
return sum(values)
def total(values):
return _total_cached(
tuple(values)
)
print(
total([1, 2, 3])
)
ما العلاقة بين equality وhash؟
إذا كان كائنان Hashable ومتساويين:
a == b
فيجب أن يكون لهما Hash متساوٍ:
hash(a) == hash(b)
لكن العكس ليس مضمونًا؛ Hash متساوية لا تعني أن الكائنين متساويان، لأن Hash Collisions ممكنة.
هل hash() تعطي معرفًا فريدًا؟
لا. لا تستخدم hash() كبديل لـID أو UUID. Hash مصممة لدعم هياكل البيانات والخوارزميات التي تعتمد على Hashing، وليست ضمانًا للتفرد.
خطأ شائع: تحويل List إلى str
قد ترى حلًا مثل:
key = str([1, 2])
data = {
key: "value"
}
هذا يعمل تقنيًا لأن String Hashable، لكنه ليس حلًا عامًا جيدًا. المفتاح أصبح نصًا شكله "[1, 2]" بدل أن يمثل زوج أرقام فعليًا.
خطأ شائع: استخدام Tuple رغم أن الترتيب غير مهم
إذا كانت:
("read", "write")
يجب أن تعني الشيء نفسه مثل:
("write", "read")
فقد تكون Tuple اختيارًا غير مناسب لأن الترتيب جزء من هويتها. استخدم frozenset إذا كان ترتيب العناصر غير مهم.
خطأ شائع: Tuple تحتوي List
تذكر أن:
("A", [1, 2])
غير Hashable بسبب List الداخلية.
خطأ شائع: إضافة Dictionary إلى Set
items = set()
items.add(
{"id": 1}
)
سيظهر unhashable type: 'dict'. إذا كان الهدف تتبع IDs فاستخدم:
seen_ids = set()
seen_ids.add(1)
طريقة تشخيص الخطأ خطوة بخطوة
- اقرأ السطر المذكور في Traceback.
- ابحث عن Dictionary Key أو Set أو Cache.
- اطبع
type()للقيمة التي تستخدمها كمفتاح أو عنصر. - جرّب
hash(value)أثناء التشخيص إذا كان ذلك مناسبًا. - إذا كانت List، اسأل هل تمثل قيمة ثابتة أم بيانات متغيرة.
- إذا كان الترتيب مهمًا، فكر في Tuple.
- إذا كان الترتيب غير مهم، فكر في frozenset.
- إذا كان لديك ID منطقي، استخدمه بدل تحويل سجل كامل.
- افحص العناصر الداخلية؛ Tuple قد تكون غير Hashable بسبب عنصر داخلي.
جدول حل سريع
| الحالة | الحل المناسب غالبًا |
|---|---|
| List تمثل زوجًا مرتبًا ثابتًا | Tuple |
| مجموعة قيم لا يهم ترتيبها | frozenset |
| List of Lists لإزالة التكرار | تحويل العناصر إلى Tuple للتتبع |
| سجل له ID واضح | استخدم ID كمفتاح |
| Tuple تحتوي List | اجعل العناصر الداخلية Hashable إذا كان ذلك منطقيًا |
| بيانات متغيرة باستمرار | أعد التفكير في تصميم المفتاح |
الفرق بين هذا الخطأ وlist indices must be integers
| الخطأ | السبب |
|---|---|
unhashable type: 'list' | استخدام List في موضع يحتاج كائنًا Hashable. |
list indices must be integers or slices, not str | استخدام نوع غير مناسب كفهرس للوصول إلى عنصر داخل List. |
روابط داخلية مفيدة من بايثون العرب
مصادر رسمية للتوسع
- تعريف Hashable في توثيق بايثون
- توثيق Dictionary في بايثون
- توثيق set وfrozenset
- توثيق hash()
- توثيق lru_cache
الخلاصة
خطأ TypeError: unhashable type: 'list' يعني أن List استخدمت في مكان يحتاج إلى قيمة Hashable. أكثر الحالات شيوعًا هي مفاتيح Dictionary وعناصر Set وList of Lists عند محاولة استخدام set().
إذا كانت List تمثل تسلسلًا ثابتًا ومرتبًا، فقد تكون Tuple مناسبة. وإذا كان ترتيب القيم لا يهم، فقد تكون frozenset أفضل. وإذا كانت البيانات سجلًا متغيرًا، فغالبًا الأفضل استخدام ID أو مفتاح منطقي أصغر بدل تحويل السجل كله.
وتذكر أن Tuple ليست Hashable دائمًا؛ يجب أن تكون عناصرها Hashable أيضًا. لذلك Tuple تحتوي List ستؤدي إلى الخطأ نفسه.
{alertSuccess} القاعدة المهمة: لا تجعل هدفك مجرد إخفاء TypeError. اختر نوع المفتاح الذي يمثل معنى البيانات فعلًا: Tuple للترتيب الثابت، frozenset للمجموعة غير المرتبة، أو ID عندما توجد هوية واضحة للسجل.
أسئلة شائعة
ما معنى TypeError: unhashable type: 'list'؟
تعني أنك استخدمت List في موضع يحتاج إلى كائن Hashable، مثل مفتاح Dictionary أو عنصر داخل Set.
لماذا لا يمكن استخدام List كمفتاح Dictionary؟
لأن List غير Hashable، بينما Dictionary تحتاج مفاتيح Hashable.
كيف أحول List إلى مفتاح Dictionary؟
إذا كانت القائمة تمثل قيمة ثابتة ومرتبة وكانت عناصرها Hashable، يمكنك استخدام tuple(my_list). وإذا كان لديك ID واضح فقد يكون استخدامه أفضل.
هل Tuple دائمًا Hashable؟
لا. Tuple تحتوي عنصرًا غير Hashable مثل List تصبح غير Hashable أيضًا.
لماذا set(list_of_lists) لا تعمل؟
لأن كل عنصر داخل القائمة الخارجية هو List غير Hashable، بينما عناصر Set يجب أن تكون Hashable.
كيف أزيل التكرار من List of Lists؟
حوّل كل قائمة داخلية إلى Tuple للتتبع. وإذا أردت الحفاظ على ترتيب أول ظهور، استخدم Set باسم seen مع List منفصلة للنتيجة.
ما الفرق بين Tuple وfrozenset كمفتاح؟
Tuple تعتبر الترتيب جزءًا من القيمة، بينما frozenset لا تهتم بالترتيب.
ما الأنواع Unhashable الشائعة؟
من أشهرها list وdict وset.
ما الأنواع Hashable الشائعة؟
من أمثلتها int وfloat وstr وbytes وTuple إذا كانت عناصرها Hashable وfrozenset.
لماذا يظهر الخطأ مع lru_cache؟
لأن Arguments المستخدمة لتكوين مفتاح Cache يجب أن تكون Hashable، وتمرير List مباشرة قد يسبب هذا الخطأ.
هل str(list) حل صحيح؟
قد يعمل تقنيًا، لكنه ليس حلًا عامًا جيدًا. اختر مفتاحًا يمثل معنى البيانات بدل تحويلها إلى نص فقط لإخفاء الخطأ.
هل hash() تعطي قيمة فريدة؟
لا. Hash ليست ID فريدًا ولا يجب استخدامها بدل UUID أو معرف منطقي.
كيف أعرف إذا كان كائن Hashable؟
يمكنك تجربة hash(value) ومعالجة TypeError، أو استخدام collections.abc.Hashable في حالات مناسبة.



