مرحبًا بك في درس جديد من سلسلة مشكلة وحل على موقع بايثون العرب. في هذا المقال سنشرح واحدًا من الأخطاء الشائعة عند التعامل مع النصوص والقوائم في Python، وهو خطأ:
AttributeError: 'list' object has no attribute 'lower'
يظهر هذا الخطأ غالبًا عندما تحاول استخدام الدالة lower() على قائمة List بدل استخدامها على نص واحد من نوع String. والمشكلة هنا ليست في الدالة نفسها، بل في نوع البيانات الذي استخدمت الدالة عليه.
في هذا الدرس سنفهم معنى الخطأ، وسبب ظهوره، والفرق بين String و List في هذه الحالة، ثم نشرح أكثر من طريقة صحيحة لحل المشكلة باستخدام الفهرسة، أو for loop، أو list comprehension، مع أمثلة عملية مناسبة للمبتدئين.
{getToc} $title={محتوى المقال}
{alertInfo} هذا الخطأ لا يعني أن الكود صعب، بل يعني أنك استخدمت دالة خاصة بالنصوص على قائمة كاملة بدل استخدامها على عنصر نصي داخل القائمة.
إذا كنت جديدًا في القوائم، أنصحك بمراجعة درس: أساسيات بايثون 16: شرح Lists في Python للمبتدئين لأن فهم القوائم سيساعدك كثيرًا في حل هذا الخطأ.
قصة المشكلة ببساطة
لنفترض أن لديك قائمة أسماء، وتريد تحويل كل الأسماء إلى حروف صغيرة حتى تجعل البحث أو المقارنة أسهل.
قد تكتب الكود التالي:
names = ["Ahmed", "Sara", "Omar"]
names = names.lower()
print(names)
لكن عند تشغيل الكود ستظهر رسالة الخطأ:
AttributeError: 'list' object has no attribute 'lower'
السبب أن المتغير names ليس نصًا واحدًا، بل قائمة تحتوي على أكثر من نص. والدالة lower() تعمل على النصوص، ولا تعمل على القائمة كاملة.
ما معنى AttributeError في Python؟
خطأ AttributeError يعني أنك حاولت استخدام خاصية أو دالة غير موجودة في نوع البيانات الحالي.
في حالتنا، الرسالة تقول:
'list' object has no attribute 'lower'
ومعناها بالعربي: الكائن من نوع list لا يمتلك دالة اسمها lower.
بمعنى أوضح: بايثون تقول لك إن lower() ليست من دوال القوائم، بل هي من دوال النصوص.
ما وظيفة lower في Python؟
الدالة lower() تستخدم مع النصوص لتحويل الحروف الإنجليزية الكبيرة إلى حروف صغيرة.
مثال صحيح:
name = "AHMED"
small_name = name.lower()
print(small_name)
الناتج:
ahmed
هنا الكود يعمل لأن المتغير name عبارة عن نص واحد من نوع str.
{alertSuccess} الدالة lower() تعمل مع String، لكنها لا تعمل مباشرة مع List.
الفرق بين String و List في هذه المشكلة
لفهم الخطأ جيدًا، يجب أن تفرق بين النص والقائمة:
String: نص واحد مثل"Python".List: قائمة تحتوي على عناصر متعددة مثل["Python", "Code"].
هذا كود صحيح:
word = "PYTHON"
print(word.lower())
لكن هذا كود خاطئ:
words = ["PYTHON", "CODE"]
print(words.lower())
لأن words قائمة، وليست نصًا.
لماذا لا تعمل lower مع List؟
القائمة في Python قد تحتوي على أنواع مختلفة من البيانات، وليس شرطًا أن تكون كل عناصرها نصوصًا.
مثال:
items = ["Python", 10, True]
هذه القائمة تحتوي على نص ورقم وقيمة منطقية. لذلك بايثون لا تستطيع تطبيق lower() على القائمة بالكامل، لأن القائمة نفسها ليست نصًا.
{alertWarning} لا تسأل: لماذا لا تطبق Python lower على كل عناصر القائمة تلقائيًا؟ لأن القائمة قد تحتوي على عناصر ليست نصوصًا أصلًا.
الحل الأول: استخدام lower على عنصر واحد من القائمة
إذا أردت تحويل عنصر واحد فقط داخل القائمة إلى حروف صغيرة، استخدم الفهرس للوصول إلى هذا العنصر، ثم طبّق عليه lower().
names = ["Ahmed", "Sara", "Omar"]
first_name = names[0].lower()
print(first_name)
الناتج:
ahmed
هنا استخدمنا names[0] للوصول إلى أول عنصر داخل القائمة، وهو نص، ثم استخدمنا lower() عليه.
ولو أردت العنصر الثاني:
print(names[1].lower())
الناتج:
sara
الحل الثاني: تحويل كل عناصر القائمة باستخدام for loop
إذا أردت تحويل كل الأسماء داخل القائمة إلى حروف صغيرة، استخدم حلقة for للمرور على كل عنصر، ثم طبّق lower() على كل اسم.
names = ["Ahmed", "Sara", "Omar"]
lower_names = []
for name in names:
lower_names.append(name.lower())
print(lower_names)
الناتج:
['ahmed', 'sara', 'omar']
في هذا المثال أنشأنا قائمة جديدة اسمها lower_names، ثم أضفنا إليها نسخة صغيرة الحروف من كل اسم.
الحل الثالث: استخدام list comprehension
يمكن كتابة الحل السابق بطريقة أقصر باستخدام list comprehension.
names = ["Ahmed", "Sara", "Omar"]
lower_names = [name.lower() for name in names]
print(lower_names)
الناتج:
['ahmed', 'sara', 'omar']
هذه الطريقة مختصرة ومشهورة في بايثون، لكنها قد تكون صعبة قليلًا على المبتدئ في البداية. لذلك افهم حلقة for أولًا، ثم استخدم هذه الطريقة بعد ذلك.
{alertInfo} list comprehension ليست إجبارية، لكنها طريقة جميلة ومختصرة عندما تفهم فكرة المرور على عناصر القائمة.
ماذا لو كانت القائمة تحتوي على عناصر ليست نصوصًا؟
أحيانًا تكون القائمة مختلطة، تحتوي على نصوص وأرقام وقيم أخرى. في هذه الحالة، لو طبقت lower() على كل العناصر مباشرة، سيظهر خطأ جديد عند الوصول إلى عنصر غير نصي.
مثال خاطئ:
items = ["Python", 10, "Code"]
result = [item.lower() for item in items]
print(result)
هذا الكود سيتوقف عند الرقم 10، لأن الرقم لا يمتلك دالة lower().
الحل: التأكد من نوع العنصر باستخدام isinstance
يمكن استخدام isinstance() للتأكد أن العنصر نص قبل تطبيق lower() عليه.
items = ["Python", 10, "Code"]
result = []
for item in items:
if isinstance(item, str):
result.append(item.lower())
else:
result.append(item)
print(result)
الناتج:
['python', 10, 'code']
بهذه الطريقة تم تحويل النصوص فقط إلى حروف صغيرة، وبقي الرقم كما هو.
{alertWarning} إذا كانت القائمة تحتوي على أنواع بيانات مختلفة، تأكد من نوع العنصر قبل استخدام lower().
مثال عملي: البحث داخل قائمة أسماء
من أشهر استخدامات lower() أنها تساعد في البحث بدون الاهتمام بحالة الحروف. مثلًا لو كانت القائمة تحتوي على "Ahmed"، والمستخدم كتب "ahmed"، نريد أن يعتبر البرنامج الاسمين متطابقين.
names = ["Ahmed", "Sara", "Omar"]
search_name = "ahmed"
lower_names = [name.lower() for name in names]
if search_name in lower_names:
print("الاسم موجود")
else:
print("الاسم غير موجود")
الناتج:
الاسم موجود
هنا قمنا بتحويل الأسماء إلى حروف صغيرة قبل البحث، لذلك لم تعد حالة الحروف تسبب مشكلة.
حل آخر بدون إنشاء قائمة جديدة
يمكنك أيضًا البحث داخل الحلقة مباشرة، والتوقف عند العثور على الاسم.
names = ["Ahmed", "Sara", "Omar"]
search_name = "ahmed"
found = False
for name in names:
if name.lower() == search_name:
found = True
break
if found:
print("الاسم موجود")
else:
print("الاسم غير موجود")
هذا الحل مناسب إذا كانت القائمة كبيرة، لأن البرنامج يتوقف بمجرد العثور على الاسم المطلوب.
كيف تعرف نوع المتغير؟
إذا لم تكن متأكدًا هل المتغير نص أم قائمة، استخدم الدالة type().
names = ["Ahmed", "Sara"]
name = "Ahmed"
print(type(names))
print(type(name))
الناتج:
<class 'list'>
<class 'str'>
معرفة نوع المتغير خطوة مهمة جدًا عند قراءة أخطاء Python، لأنها تساعدك على فهم لماذا دالة معينة لا تعمل مع قيمة معينة.
جدول سريع: سبب الخطأ والحل
| الكود | الحالة | السبب |
|---|---|---|
"Python".lower() |
صحيح | لأن القيمة نص من نوع String. |
["Python"].lower() |
خطأ | لأن القيمة قائمة من نوع List. |
["Python"][0].lower() |
صحيح | لأننا وصلنا إلى العنصر النصي داخل القائمة. |
[name.lower() for name in names] |
صحيح إذا كانت العناصر نصوصًا | لأننا نطبق lower على كل عنصر نصي وليس على القائمة نفسها. |
أخطاء شائعة مرتبطة بنفس الفكرة
قد لا تظهر المشكلة دائمًا مع lower() فقط. أحيانًا تظهر مع دوال نصية أخرى مثل strip() أو replace().
مثال:
AttributeError: 'list' object has no attribute 'strip'
أو:
AttributeError: 'list' object has no attribute 'replace'
والسبب غالبًا هو نفسه: استخدام دالة خاصة بالنصوص على قائمة كاملة بدل استخدامها على عنصر نصي داخل القائمة.
{alertError} لا تستخدم دوال النصوص مثل lower و strip و replace على List مباشرة. استخدمها على عنصر نصي داخل القائمة أو مر على العناصر باستخدام حلقة.
مثال عملي مع strip و lower معًا
أحيانًا يكون النص فيه مسافات زائدة وحروف كبيرة، لذلك نستخدم strip() لإزالة المسافات من البداية والنهاية، ثم lower() لتوحيد حالة الحروف.
names = [" Ahmed ", " SARA ", " Omar "]
clean_names = []
for name in names:
clean_names.append(name.strip().lower())
print(clean_names)
الناتج:
['ahmed', 'sara', 'omar']
هذا المثال مفيد جدًا عند التعامل مع إدخالات المستخدم، لأن المستخدم قد يكتب الاسم بمسافات زائدة أو بحروف كبيرة.
أفضل طريقة للحل حسب الحالة
| الحالة | الحل المناسب | مثال مختصر |
|---|---|---|
| تريد تحويل نص واحد | استخدم lower() مباشرة |
name.lower() |
| تريد تحويل عنصر واحد داخل قائمة | استخدم الفهرس | names[0].lower() |
| تريد تحويل كل عناصر القائمة | استخدم for loop |
for name in names |
| تريد حلًا مختصرًا | استخدم list comprehension |
[name.lower() for name in names] |
| القائمة فيها أنواع مختلفة | استخدم isinstance() |
isinstance(item, str) |
تمرين بسيط
ما الخطأ في الكود التالي؟
words = ["HELLO", "PYTHON", "CODE"]
print(words.lower())
الخطأ أن words قائمة، ولا يمكن استخدام lower() عليها مباشرة.
الحل:
words = ["HELLO", "PYTHON", "CODE"]
lower_words = [word.lower() for word in words]
print(lower_words)
الناتج:
['hello', 'python', 'code']
أسئلة شائعة
ما سبب خطأ list object has no attribute lower في Python؟
السبب أنك استخدمت الدالة lower() على قائمة List، بينما هذه الدالة تعمل على النصوص String فقط.
كيف أحول كل عناصر List إلى حروف صغيرة؟
استخدم حلقة for أو list comprehension لتطبيق lower() على كل عنصر نصي داخل القائمة.
هل lower تعمل مع الأرقام؟
لا. الدالة lower() خاصة بالنصوص فقط، ولا تعمل مع الأرقام أو القوائم مباشرة.
كيف أتأكد أن العنصر نص قبل استخدام lower؟
استخدم isinstance(item, str) للتأكد أن العنصر من نوع String قبل تطبيق lower() عليه.
هل يمكن استخدام lower مع النص العربي؟
الدالة lower() مفيدة غالبًا مع الحروف الإنجليزية لأنها تحول الحروف الكبيرة إلى صغيرة. أما النص العربي فلا توجد فيه حروف كبيرة وصغيرة بنفس مفهوم الإنجليزية، لذلك قد لا تلاحظ تغييرًا واضحًا.
روابط مفيدة
- أساسيات بايثون 16: شرح Lists في Python للمبتدئين
- الفرق بين append و extend في Python للمبتدئين
- مشكلة وحل 7: حل خطأ TypeError can only concatenate str not int to str
- حل خطأ ValueError invalid literal for int في Python
- تشغيل بايثون أون لاين بدون تثبيت
- محرر بايثون العرب لتجربة الأكواد أونلاين
- توثيق Python الرسمي حول دوال النصوص
الخلاصة
خطأ AttributeError: 'list' object has no attribute 'lower' يظهر عندما تحاول استخدام lower() على قائمة بدل نص.
الحل ليس أن تحذف القائمة أو تغير الدالة عشوائيًا، بل أن تفهم نوع البيانات:
- إذا كانت القيمة نصًا، استخدم
lower()مباشرة. - إذا كانت القيمة قائمة، استخدم
lower()على عنصر داخل القائمة. - إذا أردت تحويل كل عناصر القائمة، استخدم
for loopأوlist comprehension. - إذا كانت القائمة مختلطة، استخدم
isinstance()قبل تطبيقlower().
{alertSuccess} الخلاصة السريعة: lower للنصوص وليس للقوائم. إذا كانت لديك قائمة نصوص، طبّق lower على كل عنصر داخل القائمة.